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


26

ループを逆順で適切に行うにはどうすればよいforですか?

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

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


5
sort -r前にパイプするforか、洗濯してくださいls -r
デビッドシュワルツ

回答:


27

bashまたはkshで、ファイル名を配列に入れ、その配列を逆順に繰り返します。

files=(/var/logs/foo*.log)
for ((i=${#files[@]}-1; i>=0; i--)); do
  bar "${files[$i]}"
done

上記のコードksh_arraysは、オプションが設定されている場合(kshエミュレーションモードの場合)、zshでも機能します。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

位置パラメータを使用したら、変数if; は必要ありません。を実行しshift$1あなたの呼び出しに使用し、あなたのでbarテストし[ -z $1 ]ますwhile
user1404316

@ user1404316これは、要素を昇順で繰り返す非常に複雑な方法です。また、間違っています:どちらのテスト[ -z $1 ][ -z "$1" ]有用ではありません(パラメーターが*、または空の文字列だった場合はどうでしょうか?)。いずれにせよ、彼らはここで助けにはなりません。問題は、逆の順序でループする方法です。
ジル「SO-悪であるのをやめる」

質問では、具体的には/ var / log内のファイル名を反復処理しているため、「*」または空のパラメーターはないと想定しても安全です。しかし、私は逆の順序の点で修正されています。
user1404316

7

改行を「ファンキーな文字」と見なさない限り、これを試してください。

ls /var/logs/foo*.log | tac | while read f; do
    bar "$f"
done

改行で機能する方法はありますか?(私はそれがまれであることを知っていますが、少なくとも学習のために、正しいコードを書くことが可能かどうか知りたいです。)
Mehrdad

tacを使用してフローを逆にし、改行などの不要な文字を削除したい場合は、tr -d '\ n'にパイプできます。
ヨハン

4
ファイル名に改行、バックスラッシュ、または印刷できない文字が含まれている場合、これは壊れます。mywiki.wooledge.org/ParsingLsおよびファイルの行をループする方法を参照してください(およびリンクされたスレッド)。
ジル 'SO-悪であるのをやめる'

f私の状況では、最も投票された回答が変数を破壊しました。ただし、この答えは、通常のfor回線のドロップイン置換として機能します。
セルジュStroobandt

2

誰かがスペースで区切られた文字列リストを反復処理する方法を見つけようとしている場合、これは機能します:

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

list="a bb ccc"

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

2
システムにtacがありません。堅牢かどうかはわかりませんが、$ {mylist}でxを使用しています。do revv = "$ {x} $ {revv}"; 行って
ARP

@arp tacGNU coreutilsの一部であるので、それは一種の衝撃的です。しかし、あなたのソリューションも良いものです。
ACK_stoverflow

@ACK_stoverflow Linuxユーザーランドツールのないシステムがあります。
クサラナナンダ

@Kusalananda LinuxだけがGNU coreutilsを使用していると言っていますか?笑。busyboxにもありますtactacここで-less応答の数から、OSXにはtacがないのではないかと思います。その場合、@ arpのソリューションのバリアントを使用してください-とにかく理解しやすいです。
ACK_stoverflow

@ACK_stoverflowそれは私が言っていることです、はい。
クサラナナンダ

1
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).

基本的に、文字列+ globに一致するファイルを見つけて、それぞれをNUL文字で終了します。ファイル名に改行またはその他の奇妙な文字が含まれている場合、findはこれを適切に処理する必要があります。

tail -r

パイプを通して標準入力を受け取り、それを反転させる(なお、tail -r印刷物のすべての標準的なデフォルトである標準出力への入力の、だけではなく、最後の10行、。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は引数をNUL文字で区切って表示することを想定しています。NUL文字はから渡されfind、で反転されtailます。

私の警告:nullで終わる文字列ではうまく動作しないと読んだことtailがあります。これはMac OS Xではうまくいきましたが、すべての* nixに当てはまることを保証することはできません。慎重に踏んでください。

また、GNU Parallelxargs代替手段としてよく使用されることにも言及する必要があります。あなたもそれをチェックアウトできます。

私は何かを逃しているかもしれないので、他の人がチャイムする必要があります


2
+1素晴らしい答え。Ubuntuはサポートしtail -rていないようですが...何か間違っていますか?
Mehrdad

1
いいえ、あなたはそうは思いません。Linuxマシンは起動していませんが、「Linuxテールマン」のクイックグーグルではオプションとして表示されません。sunakuは言及tac私はそれをしようとして代わりに、代替として
tcdyl

私はまた別の代替含めるように答えを編集した
tcdyl

フープ+1と言ったときに+1を忘れていた:(完了!そのハハについてごめんなさい:)
Mehrdad

4
tail -rOSXに固有であり、ヌル区切りの入力ではなく、改行区切りの入力を反転します。2番目のソリューションはまったく機能しません(入力をlsにパイプしているので、気にしません)。確実に動作させる簡単な修正はありません。
ジル「SO-悪であるのをやめる」

1

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

Zshでこれを行う方法は次のとおりです。

配列内の要素をループしている場合は、この構文を使用します(source

for f in ${(Oa)your_array}; do
    ...
done

O次のフラグで指定された順序の逆。a通常の配列順序です。

@Gillesが言ったようにOn、例えばを使用して、globedファイルを逆順にしますmy/file/glob/*(On)。それOnは「名前の逆順」だからです。

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 /


0

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

ls -r@David Schwartzが述べたように、簡単な方法を使用します

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

-4

これを試して:

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

それが最も簡単な方法だと思います。


3
質問は「for逆順のループ」を要求しました。
マナトワーク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.