Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm new to this… I need a script that receives a directory as a parameter and prints the names of files that have between 15 and 50 lines, but I don't know how use the wc output in an if-sentence:

for f in `ls`; do
  echo "File -> $f"
  cat $f | wc -l

How do I receive a parameter? And how do I assign a the wc output to a variable?

share|improve this question
First off, don't do for f in `ls`, please. Simply use for f in *. See: – slhck Jul 19 '13 at 16:22
up vote 7 down vote accepted

Parameters can be received as $1, $2, etc., or $* for all of them.

There is also the array $@, which is usually used as "$@" as a better version of $*. Another possible usage is ${@:3} which means "all arguments starting with 3rd".

To get a command's output, use $( ... ) or its older form ` ... `. It is generally recommended to always use $() since it can be nested, e.g. blah=$(cat /blah/$(blah)/blah).

#!/usr/bin/env bash

for dir in "$@"; do
    for file in "$dir"/*; do
        lines=$(wc -l < "$file")
        if (( lines >= 15 && lines <= 50 )); then
            echo "File -> $file ($lines lines)"
        # another possible syntax:
        # if [ "$lines" -ge 15 ] && [ "$lines" -le 50 ]
        # or:
        # if [[ "$lines" -ge 15 && "$lines" -le 50 ]]

Useful resources:

share|improve this answer
For the record, BSD wc (at least the one I have on OS X) pads the line count with leading spaces even when doing wc -l < "$file", which needs additional parsing work. – slhck Jul 19 '13 at 16:38
You can also use the form for dir do instead of for dir in "$@"; do – evilsoup Jul 19 '13 at 16:49
@slhck: bash automatically ignores leading and trailing whitespace if you ask it to use the variable as an integer (e.g. comparisons) or if you explicitly declare -i it so. – grawity Jul 19 '13 at 18:18
Right, except it prints File -> ./foo ( 47 lines) or similar, but that's a minor issue. – slhck Jul 19 '13 at 20:16
@slhck: That can be avoided by using declare -i or let. – grawity Jul 19 '13 at 21:00

Here's another way of doing it using awk:

#!/usr/bin/env bash
for dir in "$@"; do
  for file in "$dir"/*; do 
    awk 'END{if(NR>=15 && NR<=50){print FILENAME}}' "$file"; 

EXPLANATION: awk (and its many variants like gawk or mawk etc) read a file line by line. The variable NR is the current line number. An END{} block will be executed when the last line of the input file is reached, at which point NR will be the number of lnes in the file. Finally, FILENAME is the name of the file currently being processes. So, the script prints the file's name if the number of lines it has seen is between 15 and 50.

Similarly, you can do this with Perl:

#!/usr/bin/env bash
for dir in "$@"; do
  for file in "$dir"/*; do 
      perl -ne 'END{if($. >=15 && $. <=50){print "$ARGV\n"}}' "$file"

EXPLANATION: perl -ne will also read through a file line by line. In Perl, the current line number is stored in $. and the END block works the same way as in awk. $ARGV is the argument passed on the command line, the file name which will be printed only if it has the right number of lines.

Your best choice will probably be the bash and wc answer of @grawity but these will not be affected by the wc implementation and should run well on any *nix.

share|improve this answer
If $. is the line number, what is $,? (The new "smartmatch" operator should work as $. ~~ 15..50, although I need to test.) – grawity Jul 19 '13 at 18:15
@grawity a typo, thanks. – terdon Jul 19 '13 at 18:16
@grawity yeah the ~~ may well work but I'm not very familiar with it and it is much more esoteric. I figured I'd give a simpler answer. – terdon Jul 19 '13 at 18:37

Generate some files:

$ for i in `seq -w 1 60`; do seq -w 1 $i > $i.lines; done

Print only those that meet the criteria:

$ wc -l * | awk '$1 >= 15 && $1 <= 50 { print $2 }' | column
15.lines        23.lines        31.lines        39.lines        47.lines
16.lines        24.lines        32.lines        40.lines        48.lines
17.lines        25.lines        33.lines        41.lines        49.lines
18.lines        26.lines        34.lines        42.lines        50.lines
19.lines        27.lines        35.lines        43.lines
20.lines        28.lines        36.lines        44.lines
21.lines        29.lines        37.lines        45.lines
22.lines        30.lines        38.lines        46.lines
share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.