1

問題を以下のコード スニペットに切り分けました。

  1. LATEST_FILE_NAME=''を使用してスクリプトを実行すると、ヌル文字列が に割り当てられることに注意してくださいksh$LATEST_FILE_NAMEただし、スクリプトを使用して実行すると、スクリプトは変数に値を正しく割り当てますsh。これは、 の値に影響を与えます$FILE_LIST_COUNT
  2. しかし、スクリプトは KornShell (ksh) にあるため、何が問題を引き起こしているのかわかりません。
  3. 以下の行のコマンドをコメントアウトするteeと、ksh スクリプトは正常に機能し、変数に値を正しく割り当てます$LATEST_FILE_NAME
(cd $SOURCE_FILE_PATH; ls *.txt 2>/dev/null) | sort -r > ${SOURCE_FILE_PATH}/${FILE_LIST} | tee -a $LOG_FILE_PATH

ご検討ください:

1. ソース コード: script.sh

#!/usr/bin/ksh
set -vx # Enable debugging

SCRIPTLOGSDIR=/some/path/Scripts/TEST/shell_issue
SOURCE_FILE_PATH=/some/path/Scripts/TEST/shell_issue
# Log file
Timestamp=`date +%Y%m%d%H%M`
LOG_FILENAME="TEST_LOGS_${Timestamp}.log"
LOG_FILE_PATH="${SCRIPTLOGSDIR}/${LOG_FILENAME}"
## Temporary files
FILE_LIST=FILE_LIST.temp    #Will store all  extract filenames
FILE_LIST_COUNT=0           # Stores total number of  files

getFileListDetails(){
    rm -f $SOURCE_FILE_PATH/$FILE_LIST 2>&1 | tee -a $LOG_FILE_PATH

    # Get list of all files, Sort in reverse order, and store names of the  files line-wise. If no files are found, error is muted.
    (cd $SOURCE_FILE_PATH; ls *.txt 2>/dev/null) | sort -r > ${SOURCE_FILE_PATH}/${FILE_LIST} | tee -a $LOG_FILE_PATH

    if [[ ! -f $SOURCE_FILE_PATH/$FILE_LIST ]]; then
        echo "FATAL ERROR - Could not create a temp file for  file list.";exit 1;
    fi

    LATEST_FILE_NAME="$(cd $SOURCE_FILE_PATH; head -1 $FILE_LIST)";
    FILE_LIST_COUNT="$(cat $SOURCE_FILE_PATH/$FILE_LIST | wc -l)";

}

getFileListDetails;
exit 0;

2. shell を使用した場合の出力 sh script.sh:

+ getFileListDetails
+ rm -f /some/path/Scripts/TEST/shell_issue/FILE_LIST.temp
+ tee -a /some/path/Scripts/TEST/shell_issue/TEST_LOGS_201304300506.log
+ cd /some/path/Scripts/TEST/shell_issue
+ sort -r
+ tee -a /some/path/Scripts/TEST/shell_issue/TEST_LOGS_201304300506.log
+ ls 1.txt 2.txt 3.txt
+ [[ ! -f /some/path/Scripts/TEST/shell_issue/FILE_LIST.temp ]]
cd $SOURCE_FILE_PATH; head -1 $FILE_LIST
++ cd /some/path/Scripts/TEST/shell_issue
++ head -1 FILE_LIST.temp
+ LATEST_FILE_NAME=3.txt
cat $SOURCE_FILE_PATH/$FILE_LIST | wc -l
++ cat /some/path/Scripts/TEST/shell_issue/FILE_LIST.temp
++ wc -l
+ FILE_LIST_COUNT=3
exit 0;
+ exit 0

3. ksh を使用した場合の出力 ksh script.sh:

+ getFileListDetails
+ tee -a /some/path/Scripts/TEST/shell_issue/TEST_LOGS_201304300507.log
+ rm -f /some/path/Scripts/TEST/shell_issue/FILE_LIST.temp
+ 2>& 1
+ tee -a /some/path/Scripts/TEST/shell_issue/TEST_LOGS_201304300507.log
+ sort -r
+ 1> /some/path/Scripts/TEST/shell_issue/FILE_LIST.temp
+ cd /some/path/Scripts/TEST/shell_issue
+ ls 1.txt 2.txt 3.txt
+ 2> /dev/null
+ [[ ! -f /some/path/Scripts/TEST/shell_issue/FILE_LIST.temp ]]
+ cd /some/path/Scripts/TEST/shell_issue
+ head -1 FILE_LIST.temp
+ LATEST_FILE_NAME=''
+ wc -l
+ cat /some/path/Scripts/TEST/shell_issue/FILE_LIST.temp
+ FILE_LIST_COUNT=0
exit 0;+ exit 0
4

1 に答える 1

2

わかりました、ここに行きます...これはトリッキーで微妙なものです。その答えは、パイプラインの実装方法にあります。POSIXは次のように述べています。

パイプラインがバックグラウンドにない場合 (非同期リストを参照)、シェルはパイプラインで指定された最後のコマンドが完了するまで待機し、すべてのコマンドが完了するまで待機することもあります)。

キーワードmayに注意してください。多くのシェルでは、すべてのコマンドが完了する必要がある方法でこれを実装しています。たとえば、のマンページを参照してください。

シェルは、値を返す前に、パイプライン内のすべてのコマンドが終了するのを待ちます。

マンページの文言に注意してください。

最後のコマンドを除いて、各コマンドは個別のプロセスとして実行されます。シェルは最後のコマンドが終了するのを待ちます。

あなたの例では、最後のコマンドはteeコマンドです。tee前にコマンドでリダイレクトstdoutしたので入力がないので${SOURCE_FILE_PATH}/${FILE_LIST}即終了。単純化して言えば、teeは以前のリダイレクトよりも高速です。つまり、ファイルから読み取るまでにファイルへの書き込みが完了していない可能性があります。sleepコマンド全体の最後にa を追加することで、これをテストできます (これは修正ではありません!) 。

$ ksh -c 'ls /tmp/* | sort -r > /tmp/foo.txt | tee /tmp/bar.txt; echo "[$(head -n 1 /tmp/foo.txt)]"'
[]

$ ksh -c 'ls /tmp/* | sort -r > /tmp/foo.txt | tee /tmp/bar.txt; sleep 0.1; echo "[$(head -n 1 /tmp/foo.txt)]"'
[/tmp/sess_vo93c7h7jp2a49tvmo7lbn6r63]

$ bash -c 'ls /tmp/* | sort -r > /tmp/foo.txt | tee /tmp/bar.txt; echo "[$(head -n 1 /tmp/foo.txt)]"'
[/tmp/sess_vo93c7h7jp2a49tvmo7lbn6r63]

そうは言っても、他にも考慮すべき点がいくつかあります。

  1. グロビングや単語分割 (パスにスペースが含まれている場合) などの問題を回避するために、特にファイルを扱う場合は常に変数を引用符で囲んでください。

    do_something "${this_is_my_file}"

  2. head -1非推奨です。使用してくださいhead -n 1

  3. 1行にコマンドが1つしかない場合、最後のセミコロン;は不要です...スキップしてください

  4. LATEST_FILE_NAME="$(cd $SOURCE_FILE_PATH; head -1 $FILE_LIST)"

    最初にディレクトリに移動する必要はありません。cdパス全体を引数として指定するだけheadです:

    LATEST_FILE_NAME="$(head -n 1 "${SOURCE_FILE_PATH}/${FILE_LIST}")"

  5. FILE_LIST_COUNT="$(cat $SOURCE_FILE_PATH/$FILE_LIST | wc -l)"

    これは不要なCatの使用と呼ばれます。なぜなら、ファイルを処理できるからです。の出力にはファイル名が含まれているため、おそらくこれを使用しましたが、代わりにeg を使用できます。catwcwc -l myfileFILE_LIST_COUNT="$(wc -l < "${SOURCE_FILE_PATH}/${FILE_LIST}")"

さらに、 Why you should not parse the output of ls(1)およびHow can I get the newest (or Oldest) file from a directory?を読みたいと思うでしょう。.

于 2013-05-01T17:11:55.997 に答える