おそらく空白を含むファイル名のリストを扱うPOSIX準拠の方法


14

空白を含むファイル名を操作するための配列の使用を提案するBashスクリプトガイドを見てきました。ただし、DashAsBinShは配列には移植性がないことを示唆しているため、空白を含むファイル名のリストを扱うPOSIX準拠の方法を探しています。

以下のスクリプト例を修正して、 echo

foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar

ここにスクリプトがあります

#!/usr/bin/env sh

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps

dostuffwith() { echo $1; };

F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)

for f in $ALL_FILES
do
    fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
    dostuffwith $fpath
done

回答:


8

(位置パラメータ:POSIXシェルは、一つのアレイ有し$1$2集合的に参照さなど、"$@")。

set -- 'foo/target/a.jar' 'foo/target/b.jar' 'bar/target/b.jar' 'bar/target/lol whitespace.jar'
set -- "$@" '/another/one at the end.jar'

for jar do
  dostuffwith "$jar"
done

これは1つしかないため不便であり、位置パラメータのその他の使用をすべて破棄します。位置パラメータは関数に対してローカルであり、それは時には祝福であり、時には呪いです。

ファイル名に改行が含まれないことが保証されている場合は、区切り文字として改行を使用できます。変数を展開するときは、最初にグロビングをオフにしset -f、フィールド分割文字のリストにIFS改行のみが含まれるように設定します。

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"

set -f; IFS='
'                           # turn off variable value expansion except for splitting at newlines
for jar in $INPUT; do
  set +f; unset IFS
  dostuffwith "$jar"        # restore globbing and field splitting at all whitespace
done
set +f; unset IFS           # do it again in case $INPUT was empty

リスト内のアイテムを改行で区切ると、特に多くのテキスト処理コマンドを便利に使用できますsort

明示的にフィールド分割を行う場合を除いて、変数置換を常に二重引用符で囲むことを忘れないでください(それをオフにしない限り、グロビングも同様です)。


良い答えと説明。これにより、元のsort | uniqステップが意図したとおりに機能するため、これを承認済みとしてマークします。
エーロアールトネン

5

$INPUT変数は区切り文字として改行を使用しているため、ファイルには名前に改行が含まれないと想定します。そのため、はい、ファイルを繰り返し処理して空白を保持する簡単な方法があります。

考えは、readシェルの組み込みを使用することです。通常read、空白で分割されるため、スペースで分割されます。ただし、設定することはできますがIFS=$'\n'、代わりに改行のみで分割されます。したがって、リスト内の各行を反復処理できます。

ここに私が思いつくことができる最小のソリューションがあります:

INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"

dostuffwith() {
    echo "$1"
}

echo "$INPUT" | awk -F/ '{if (!seen[$NF]++) print }' | \
while IFS=$'\n' read file; do
  dostuffwith "$file"
done

基本的awkには、ファイル名に基づいて重複排除を行う「$ INPUT」を送信します(/最後のアイテムがまだ表示されていない場合、分割してから行を印刷します)。次に、awkがファイルパスのリストを生成したら、リストwhile readを反復処理するために使用します。


$ checkbashismsはbar.shライン14(<<<ここでは、文字列)で可能bashismをbar.sh
エーロアールトネン

1
@EeroAaltonen herestringを使用しないように変更しました。ただし、この変更により、whileループdostuffwithがサブシェルで実行されることに注意してください。そのため、実行中のシェルに加えられた変数または変更は、ループが完了すると失われます。唯一の選択肢は、完全なヒアドキュメントを使用することです。これはそれほど不快ではありませんが、これが望ましいと思いました。
パトリック

小ささよりも読みやすさに基づいてポイントを授与しています。これは確かに機能し、そのためにすでに+1されています。
エーロアールトネン

IFS="\n"バックスラッシュとn文字で分割します。しかし、read fileでは、分割はありません。IFS="\n"入力の最初と最後に削除された空白文字を$ IFSから削除するという点で依然として有用です。行を読み取るための正規の構文はIFS= read -r lineですIFS=anything read -r line(ただし、空白が含まれいない場合)。
ステファンシャゼル14年

おっとっと。どうやってそれを管理したのかわかりません。修繕。
パトリック14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.