成功した場合、コマンドの出力をパイプします


14
INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml | head -1 | xargs basename`

head -1最初のコマンドが成功した場合にのみ、2番目のコマンド()を実行したかった。このコマンドを改善するにはどうすればよいですか?


成功するとはどういう意味ですか?ls失敗しません。
マッテオ

2
ただし、一致するファイルがない場合、グロブは失敗する可能性があります。
トリプリー

回答:


8

これを試して:

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml | head -1 | xargs -r basename`

フラグを渡すとxargs、標準入力()から少なくとも1つの項目を読み取る場合-rにのみ実行さbasenamehead -1ます。

head -1 実行されますが、出力は表示またはキャプチャされません。

また、からのエラー出力をユーザーに見せたくない場合はlslsのstderrストリームをにリダイレクトできます/dev/null

INPUT_FILE=`ls -rt "$MY_DIR"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename`

周りに引用符を追加したことにも注意してください$MY_DIR。そうすれば$MY_DIR、スペースが含まれていてもコマンドは失敗しません。

bashなどの最新のシェルを使用している場合は$( )、バックティックの代わりにキャプチャシェルを使用する必要があります。変数のスタイルを変更することも検討する必要があります。通常、スクリプトではすべて大文字の変数名を使用しないでください。このスタイルは通常、予約済み変数および環境変数用に予約されています。

input_file=$(ls -rt "$my_dir"/FILE.*.xml 2> /dev/null | head -1 | xargs -r basename)

ファイルがない場合、このb0rkはありませんか?
メル・ボイス

実際:ls -rt GLOB 2>/dev/null | head -n1 | xargs -r basename動作します。
メルボイス

@MelBoyce:それを答えに加えました。ありがとう。

lsは、ファイル情報をインタラクティブに見るためのツールです。その出力は人間向けにフォーマットされており、スクリプトにバグが発生します。代わりに、globsまたはfindを使用してください。理由を理解する:mywiki.wooledge.org/ParsingLs
cinelli

@cinelli:それは本当です。私はちょうど質問に答えていました。

9

パイプラインでは、すべてのコマンドが次々にではなく、同時に開始および実行されます。そのため、出力をどこかに保存する必要があります。

if ls_output=$(ls -rtd -- "$MY_DIR"/FILE.*.xml); then
  first_file=$(printf '%s\n' "$ls_output" | head -n 1)
  first_file_name=$(basename -- "$first_file")
fi

ファイル名に改行文字が含まれていないことを前提としていることに注意してください。使用xargsすると、空白文字、一重引用符、二重引用符、およびバックスラッシュに関する問題も意味します。変数を引用符で囲まないでおくと、スペース、タブ、ワイルドカード文字の問題が発生します。忘れる--と、ファイル名がで始まる問題が発生します-

zsh文字に関するこれらの制限なしで最も古いファイルのベース名を取得するには(コマンドへの引数のサイズが制限されるという問題も回避します):

 first_file_name=($MY_DIR/FILE.*.xml(Om[1]:t))

一致するものがない場合、そのコマンドは失敗し、スクリプトが中止されます。次のこともできます。

 first_file_name=($MY_DIR/FILE.*.xml(NOm[1]:t))

この場合、first_file_name一致する場合は配列に1つの要素が含まれ、一致しない場合は0が含まれます。その後、次のことができます。

 if (($#first_file_name)); then
    printf 'Match: %s\n' $first_file_name
 else
    echo >&2 No match
 fi

4

ディレクトリで最新の変更されたファイルを検索します。

latest() {
  local file path=${1:-.} ext=${2-} latest
  for file in "${path%/}"/*"$ext"; do 
    [[ $file -nt $latest ]] && latest=$file
  done
  [[ $latest ]] && printf '%s\n' "$latest"
}

使用法: latest [directory/path/ [.extension]]

を呼び出す代わりにbasename、パラメータ展開を使用します。

in_file=$(latest in/my/dir .xml)
base_fn=${in_file##*/} base_fn=${base_fn%.*}

これらのコンテンツを含むディレクトリ内:

foo.xml bar.xml baz.xml newest.xml

base_fn変数の内容は次のとおりです。 newest

これを適切に使用してリクエストの目的を果たすには:

check_dir=/path/to/check
check_ext=.xml
if in_file=$(latest "$check_dir" "$check_ext"); then
  base_fn=${in_file##*/} base_fn=${base_fn%.*}
else
  printf '%s\n' "No file found in $check_dir" >&2
fi

編集:質問を検討すると、問題のlsコマンドはディレクトリ内の最も古いファイルを探していることに気付きました。この同じ関数の名前を変更しoldest[[ $file -ot $oldest ]] && oldest=$file代わりに同じ効果を達成する必要があります。混乱をおaびします。

注意すべき重要なことは、人類のいかなる状況においても、絶対にlsの出力を解析すべきではないということです。決して。


- lsベースのアプローチとは反対に、シンボリックリンクがある場合、シンボリックリンクのターゲットの変更時間が考慮されることに注意してください(同じ動作を行う-Lオプションを追加しますls)。
ステファンシャゼラス

0

ここでの最良の選択肢は次のとおりだと思います。

ls -rf $MY_DIR/FILE.*.xml
if [ $? -eq 0 ]; then
      INPUT_FILE=`ls -rt $MY_DIR/FILE.*.xml|head -1|xargs basename `
else
      echo "Error!"
fi

ファイルがない場合、lsの戻りコードは2ですが、ファイルが見つかった場合は0になります。


2
代わりに比較すること$?に対して0、あなたはこれを行うことができますif ls -rf $MY_DIR/FILE.*.xml ; then

また、最初のコマンドの出力をにリダイレクトできます/dev/nullls -rf $MY_DIR/FILE.*.xml &> /dev/null。これにより、ユーザーには余分な出力が表示されなくなります。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.