web-dev-qa-db-ja.com

forループを元に戻すにはどうすればよいですか?

どうすれば適切にforループを逆の順序で実行できますか?

for f in /var/logs/foo*.log; do
    bar "$f"
done

ファイル名のファンキーな文字が壊れないソリューションが必要です。

28
user541686

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
8
sunaku

誰かがスペースで区切られた文字列リストを逆反復する方法を理解しようとしている場合、これはうまくいきます:

reverse() {
  tac <(echo "$@" | tr ' ' '\n') | tr '\n' ' '
}

list="a bb ccc"

for i in `reverse $list`; do
  echo "$i"
done
> ccc
> bb 
> a
2
ACK_stoverflow

あなたの例では、いくつかのファイルをループしていますが、配列のループや任意の数の順序に基づく逆転もカバーできるより一般的なタイトルのため、この質問を見つけました。

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 /

1
henry
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 Parallelxargsの代替としてよく使用されることにも言及する必要があります。あなたもそれをチェックするかもしれません。

私は何かが足りないかもしれないので、他の人がチャイムすべきです。

1
tcdyl

簡単な方法は、@ David Schwartzが述べたls -rを使用することです

for f in $(ls -r /var/logs/foo*.log); do
    bar "$f"
done
0
Megver83

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に変更することで回避できます(ただし、自分のコンピューターで動作するように修正した後は、正確に覚えていません)。エラーメッセージが表示され、回避策が見つからない場合は、コメントとして投稿してください。トラブルシューティングをお手伝いします。

0
Alex