5

Output to redirect in script:

$ ls -ltr |awk '{print $9}'

default.txt dfah.txt fruit.txt fruit_prices.txt dfh.txt header.txt testfile.txt topoutput.txt

Script written on shell :

while read line
do 
var=`sed -e 's/\.txt/txt\.txt/' $line`
 echo $var
 done < `ls -ltr |awk '{print $9}'`

Getting Error :

-bash: `ls -ltr |awk '{print $9}'`: ambiguous redirect

Can expert help me how ambiguous redirect happen in above code.

markling
  • 213

4 Answers4

4

try

ls -ltr |awk '{print $9}' | while read line
do 
  var=`sed -e 's/\.txt/txt\.txt/' $line`
  echo $var
done 

you gave a command like

while read line ; do
  ...
 done < a b c d

which can't be parsed

Archemar
  • 31,554
4

It looks like you're trying to do

while read line
do
  ...
done < <(ls -ltr | awk '{print $9}')

but why? Piping into while read line (Archemar's answer) is clearer and more portable.

OK, to make my answer more complete: as pointed out by Zoltán Böszörményi,

ls -ltr | awk '{print $9}' | while read line
do
  ...
done

in bash (and some other shells), runs the body of the while in a subshell, so state changes to the shell state (e.g., shell variables) will not persist beyond the loop.  This is discussed more fully in the following places:

The < <(…) code at the top of this answer is one way to avoid that problem.


But another thing

You’re doing ls -ltr | awk '{print $9}'.  That looks like a kludge to list files in order of modification date/time and then extract the file names.  A couple of issues:

  • This fails for filenames that contain whitespace.
  • You’re making extra work for yourself by telling ls to generate long listing information (file mode, owner, size, etc.) and then stripping it out, when you don’t need to generate it in the first place.

You can address both of these issues by leaving off the l option to ls, and leaving off the awk:

while read line
do
  ...
done < <(ls -tr)

This may still have problems with filenames that begin or end with whitespace.

See also Why you shouldn’t parse the output of ls(1).

1

Because "cat file | while read line ..." runs the the body of "while" in a subshell and shell variables will not be visible outside the subshell, but "while read line ; do ... ; done < <(command)" runs it in the same shell.

Here's my test file:

$ cat fruits.txt
apple
cherry
pear
plum

Watch the difference of the two scripts and their results:

$ cat a.sh
#!/bin/bash

FOUND=0

while read fruit ; do
    case $fruit in
    cherry)
        echo yay, cherry found
        FOUND=1
        ;;
    esac
done < <(cat fruits.txt)
echo cherry found: $FOUND

$ ./a.sh
yay, cherry found
cherry found: 1

but

$ cat b.sh 
#!/bin/bash

FOUND=0

cat fruits.txt | while read fruit ; do
    case $fruit in
    cherry)
        echo yay, cherry found
        FOUND=1
        ;;
    esac
done
echo cherry found: $FOUND
$ ./b.sh 
yay, cherry found
cherry found: 0
0

You could also do:

while read line; do
    var=`echo "$line" |sed -e 's/\.txt/txt\.txt/'`
    echo $var
done <<_EOT_
$(ls -ltr | awk '{print $9}')
_EOT_