どうすれば適切にfor
ループを逆の順序で実行できますか?
for f in /var/logs/foo*.log; do
bar "$f"
done
ファイル名のファンキーな文字が壊れないソリューションが必要です。
Bashまたはkshでは、ファイル名を配列に入れ、その配列を逆の順序で繰り返します。
files=(/var/logs/foo*.log)
for ((i=${#files[@]}-1; i>=0; i--)); do
bar "${files[$i]}"
done
上記のコードは、ksh_arrays
オプションが設定されています(kshエミュレーションモードです)。 zshには、glob修飾子を使用して一致の順序を逆にする、より簡単な方法があります。
for f in /var/logs/foo*.log(On); do bar $f; done
POSIXには配列が含まれていないため、移植可能にする場合は、文字列の配列を直接格納する唯一のオプションは位置パラメータです。
set -- /var/logs/foo*.log
i=$#
while [ $i -gt 0 ]; do
eval "f=\${$i}"
bar "$f"
i=$((i-1))
done
改行を「ファンキーな文字」と見なさない限り、これを試してください。
ls /var/logs/foo*.log | tac | while read f; do
bar "$f"
done
誰かがスペースで区切られた文字列リストを逆反復する方法を理解しようとしている場合、これはうまくいきます:
reverse() {
tac <(echo "$@" | tr ' ' '\n') | tr '\n' ' '
}
list="a bb ccc"
for i in `reverse $list`; do
echo "$i"
done
> ccc
> bb
> a
あなたの例では、いくつかのファイルをループしていますが、配列のループや任意の数の順序に基づく逆転もカバーできるより一般的なタイトルのため、この質問を見つけました。
Zshでこれを行う方法は次のとおりです。
配列の要素をループしている場合は、次の構文を使用します( source )
_for f in ${(Oa)your_array}; do
...
done
_
O
次のフラグで指定された順序を逆にします。 a
は通常の配列順序です。
@Gillesが言ったように、On
は、グロブされたファイルを逆順にします。 my/file/glob/*(On)
を使用します。これは、On
が「name逆順」であるためです。
Zshソートフラグ:
a
配列の順序L
ファイル長l
リンクの数m
変更日n
名前^o
_逆順(o
は通常の順序です)O
逆順例については、 https://github.com/grml/zsh-lovers/blob/master/zsh-lovers.1.txt および http://reasoniamhere.com/2014を参照してください。/01/11/outrageously-useful-tips-to-master-your-z-Shell /
find /var/logs/ -name 'foo*.log' -print0 | tail -r | xargs -0 bar
希望どおりに動作するはずです(これはMac OS Xでテストされており、以下の警告があります...)。
Findのmanページから:
-print0
This primary always evaluates to true. It prints the pathname of the current file to standard output, followed by an ASCII NUL character (charac-
ter code 0).
基本的には、文字列+グロブと一致するファイルを見つけ、それぞれをNUL文字で終了します。ファイル名に改行やその他の奇妙な文字が含まれている場合、findはこれをうまく処理するはずです。
tail -r
パイプを介して標準入力を受け取り、それを逆にします(tail -r
は、最後の10個だけでなく、stdoutへの入力のすべてを出力allすることに注意してください)。行、これは標準のデフォルトです。詳細はman tail
を参照)。
次に、それをxargs -0
にパイプします。
-0 Change xargs to expect NUL (``\0'') characters as separators, instead of spaces and newlines. This is expected to be used in concert with the
-print0 function in find(1).
ここで、xargsは、find
から渡され、tail
で反転されたNUL文字で区切られた引数を想定しています。
私の注意: tail
はnullで終了する文字列ではうまく機能しないことを読みました 。これはMac OS Xでうまく機能しましたが、すべての* nixに当てはまるとは保証できません。注意深く踏みます。
GNU Parallel がxargs
の代替としてよく使用されることにも言及する必要があります。あなたもそれをチェックするかもしれません。
私は何かが足りないかもしれないので、他の人がチャイムすべきです。
簡単な方法は、@ David Schwartzが述べたls -r
を使用することです
for f in $(ls -r /var/logs/foo*.log); do
bar "$f"
done
Mac OSXはtac
コマンドをサポートしていません。 @tcdylによる解決策は、for
ループで単一のコマンドを呼び出すときに機能します。他のすべてのケースでは、以下がそれを回避する最も簡単な方法です。
この方法では、ファイル名に改行を含めることはできません。根本的な理由は、tail -r
が入力を改行で区切られるように並べ替えることです。
for i in `ls -1 [filename pattern] | tail -r`; do [commands here]; done
ただし、改行の制限を回避する方法があります。ファイル名に特定の文字(たとえば、「=」)が含まれていないことがわかっている場合は、tr
を使用してすべての改行をこの文字に置き換え、並べ替えを実行できます。結果は次のようになります。
for i in `find [directory] -name '[filename]' -print0 | tr '\n' '=' | tr '\0' '\n'
| tail -r | tr '\n' '\0' | tr '=' '\n' | xargs -0`; do [commands]; done
注:tr
のバージョンによっては、'\0'
を文字としてサポートしない場合があります。これは通常、ロケールをCに変更することで回避できます(ただし、自分のコンピューターで動作するように修正した後は、正確に覚えていません)。エラーメッセージが表示され、回避策が見つからない場合は、コメントとして投稿してください。トラブルシューティングをお手伝いします。