エラー:出力をコマンドにパイプするときのあいまいなリダイレクト

エラー:出力をコマンドにパイプするときのあいまいなリダイレクト

スクリプトから出力がリダイレクトされました。

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

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

シェルに書かれたスクリプト:

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

エラーが発生しました:

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

上記のコードであいまいなリダイレクトがどのように発生するのか、専門家の助けを借りることができますか?

答え1

努力する

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

このコマンドを発行しました。

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

解析できません

答え2

しようとしているようです。

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

しかし、なぜ? while read lineアケマの答え)がより明確で移植性に優れています。

いいですね。答えをより完全にするには:ZoltánBöszörményiは次のように指摘しています。

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

Bash(および他の一部のシェル)では、サブジェクトはサブシェルで実行されるため、シェルのwhile状態(シェル変数など)の状態の変更はループの外側で持続しません。これについては、次の場所でさらに詳しく説明します。

< <(…)この答えの上のコードは問題を回避する1つの方法です。


しかし、もう一つ

あなたはやっていますls -ltr | awk '{print $9}'。これは、変更された日時順にファイルを一覧表示し、ファイル名を抽出するパッチワークのように見えます。いくつかの質問があります:

  • スペースを含むファイル名の場合、この操作は失敗します。
  • ls作成しようとすると、自分で追加の作業を行うことです。情報(ファイルモード、所有者、サイズなど)を一覧表示し、最初に作成する必要がない場合は削除します。

省略すると、両方の問題を解決できます。lオプション  lsと省略  awk:

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

スペースで始まるか終わるファイル名はまだ問題を引き起こす可能性があります。

また、見ることができますls(1) の出力を分析しない理由

答え3

"cat file | while read line ..."はサブシェルで "while"の本文を実行するため、シェル変数はサブシェルの外側には表示されませんが、 "while read line ; do ... ; did < <(command ) " を使用して同じシェルで実行します。

これは私のテストファイルです。

$ cat fruits.txt
apple
cherry
pear
plum

両方のスクリプトと結果の違いを見てください。

$ 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

しかし、

$ 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

答え4

次のようにすることもできます。

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

関連情報