シェルスクリプト総合 その27
■ このスレッドは過去ログ倉庫に格納されています
シェルスクリプトの総合スレです。 □お約束 ・特記なき場合はBourne Shell(/bin/sh)もしくはPOSIX準拠の互換シェルがデフォルトです。 bash/zsh/ksh/ash/dash/yash/poshなどの専用機能に依存する場合は明示しましょう。 Linuxユーザは/bin/shの正体がbashまたはdashなので特に注意。 FreeBSDユーザは/bin/shの正体がashなので注意。 ・POSIXについてのリンクは https://en.wikipedia.org/wiki/POSIX にまとめられています 最新の仕様はこちらへ http://pubs.opengroup.org/onlinepubs/9699919799/ (左上の「Shell & Utilities」 から参照することができます。) ・v7 shに一番近くて、現役(?)のshは、OpenSolaris由来のheirloom sh。 http://src.illumos.org/source/xref/illumos-gate/usr/src/cmd/sh/ http://heirloom.sourceforge.net/sh.html ・csh/tcshのシェルスクリプトは推奨されません。 (理由は「csh-whynot」でググれ) ・UNIXにはシェルスクリプトに便利な小さなコマンドがいろいろあります。 manや参考リンクを見ましょう。 aproposないしはman -kでそれらしい単語による簡単な検索もできます。 ・シェルで使えるワイルドカード等は正規表現ではありません。 正規表現の話題はスレ違い(正規表現スレへ) ・シェルスクリプトのことをシェルってゆうな □初心者へのアドバイス: ・適した道具を判断するのも頭の重要な使い方。シェルスクリプトよりも awkまたはperlの方が適した処理にはそちらを使いましょう。 ・知らないコマンドが出てきたらmanを引きましょう。 ・思い通りに動かないときは、まずは sh -x でトレースしましょう。 □回答者への注意事項: ・シェルスクリプトでの処理方法を質問しているのに、よくわからずに 「そういうのはperl使いましょう」と回答するのはやめましょう。 安易にperlに逃げずにシェルスクリプトで処理するのが頭のいいやり方。 質問に対して問題が間違ってるといちゃもんをつけるのもやめましょう 前スレ シェルスクリプト総合 その26 https://mevius.5ch.net/test/read.cgi/unix/1489979246/ >>593 を〜、grep -oで切り出しできるのね 久々にこのスレで参考になりました コロンを含むディレクトリを$PATHに登録した場合ってどういう挙動になるんだろう findでexecオプションの引数のあとにシェルに渡すパイプを付けるとexecに渡したコマンドがシグナル13パイプ破壊を出してくるんですけど どうにかなりませんかね。 今のところ/dev/nullに標準エラー出力を捨てることで解決してるんですけども。 find . -exec basename \{\} \; | head ↑これで再現するはずです。 解決するときはできればPOSIXの範囲でやりたいです。findのGNU拡張で解決できるならそれでもいいんですが メインPCがOS Xなので、最低でもBSD拡張、さらに言えばPOSIXに限定してほしいです すいません。 FreeBSDは再現しない。 CentOSは再現した。 対策は後で考える。 なにか最近やけに POSIX にこだわってる奴が多いが同一人物か? >>613 単純にPOSIXの価値というかシェルスクリプト全体の有用性が見直されているだけでは たとえば*BSDのスレでGNUライセンスに拘ったレスが連続するのはおかしいが BSDライセンスを重視するスレがたくさんあっても別におかしくはないだろう パイプでなのでfindとhead が同時にプロセスとして存在 findが標準出力に出力するとパイプを通して/パイプとして繋がってるheadの標準入力に入力として headが目的を達して終了=パイプが無くなる、だがしかし、findは出力を続けようとし出力しようとしたらパイプが壊れてるうううっ 普通何もしなくても、パイプが損失したらSIGPIPEが飛んできて(強制)終了するんだけど(フィルタとしてもなUNIX的な望ましいデフォルト動作)、なぜかSIGPIPE無視して続けようという謎動作? findとheadが直接は繋がっていなくてかもしれんが。パイプの送出側がSIGPIPEを無視って謎動作なのは変わらないかな >>615 basenameが標準出力につながってるだけだから、findはwriteしないのでSIGPIPEを受け取らないよ。 basenameが事故死したのをfindが報告してるだけ。 というわけで、basenameが事故死したらfindを続けるのをやめるようにしてみた。 この方が無駄にbasenameを続けるよりよかろう。 find /var \( -exec basename {} \; -o -quit \) | head ただし、最初の事故死についてだけはfindがおせっかいに報告してしまう。 あとはまかせた。 >>616 そのへんが実装によりちょっと違うってとこなのかなあ。出る出ないは >>613 自分側で「なにか違って」動かなかったらめんどくさいってだけじゃないの、単に CentOS$ strings /bin/find | grep -i signal signal %s terminated by signal %d FreeBSD$ strings /usr/bin/find | grep -i signal なんもなし 以上、findのおせっかい度の差。 すまん間違えて送信しちゃった $ find /etc -exec sh -c 'basename {}' \; | head これでどうだろう。 -quitオプションはPOSIXの範疇ではないけどこれはPOSIXに準拠してる ちなみに>>610 のコマンドラインはDebian GNU/Linuxのfind 4.7で再現した。 >>619 ,620 なーるほどっ ww なるほどっ、なっとく >>621 ん? それSIGPIPEは回避できるけど別の問題が発生しない? 「basename: 余分な演算子 XXX」←みたいに怒られるんだが あと $ find /etc -exec sh -c 'echo {}' \; | head ↑これをやるとやっぱりSIGPIPEが出されるようだ。 >>613 どこかの先生が課題にそう書いてるんだろう。 ちゃんと調べないで持ってくるバカ学生が多いんじゃないの。 >>610 >findでexecオプションの引数のあとにシェルに渡すパイプを付けるとexecに渡したコマンドがシグナル13パイプ破壊を出してくるんですけど この動作は、POSIX的にはどうなの? まずいというならPOSIX的にどうまずいの? find . -exec basename \{\} \; | { head; cat >/dev/null; } -execにこだわりがないのであれば、 find . -print0 | xargs -0 -L 1 basename | head 今度はxargsがお節介メッセージ出すけど、findはheadが終了したら終了する xargs(1)に-0オプションはない(POSIX厨) findにも-print0は無いけどな。BSDにもあるからいいんじゃね find . -print | xargs -n1 basename 2>/dev/null | head >>634 こういうPOSIXにやたらこだわってるのって なにかの精神障碍があるんじゃないの? 一度そういう病院で診てもらえ。 >>616 find . -exec sh -c 'trap "exit" pipe; basename {}' \; -o -quit | head >>638 試してないけど、trapせずにbasenameのあとにexitするだけでだめなん? find /etc -exec basename \{\} \; |& head これでエラーも出ず余計な出力も引っ掛らなかったんだけど 理屈が分からん |& は標準出力と標準エラー出力両方を通すはずなので SIGPIPEのエラーが出力されるはずだがgrepしても見当らない find自身がSIGPIPEで終了してんじゃない 実行時間も短くなってるし >>641 すいません。それどうやって確かめたらいいですかね Linux なら strace で見ると分かる $ strace -f bash -c 'find /etc -exec basename \{\} \; |& head' stderrが(標準出力)のパイプになりの、そのパイプが無くなり、find自身がエラーメッセージをそれに書き込もうとしてSIGPIPEを受けるってとこか find . -exec basename {} \; -o -print | head としても同じように終わるってとこで >>640 だれかこれをmacOSでやってくれない? あれは確かSIGPIPEを受けとらないから GNU findと違ってこの書き方だと不具合が起こりそうだ >>646 >あれは確かSIGPIPEを受けとらない いや、それはないだろう。単に>>621 の違いじゃないの macOS Sierra標準のbashはまだ |& をサポートしてないヤツみたいなので(??)、2>&1 | としたが同じくfindは終わる ああ、|& は元々はzshのなのか(?) macOS Sierra標準のzshでは/でも、>>640 のままで特に何も問題なく終わった >>649 |&はcsh由来。zshはパクっただけ。 それはPOSIXですか?POSIXじゃないのですか? ところで俺のPOSIXを見てくれ。こいつをどう思う? まあPOSIXじゃないので質問者の要請は満たしてないよね 俺が提案するとしたらexecは使わず find . | xargs -I @ basename @ | head ↑これ。 それじゃあかんだろ。xargsのオプションが足りない 俺がやるなら xargs -I には {} を 指定するがな @というのは見たことがない 社内規約かなにかかな? この問題って本質的には、 find は /etc の下の全部のファイル(10以上)を返すが、 head は 10行で打ち切ってしまうという矛盾にある。 そこは目をつぶるって方針なんだから、最初に書いてあったstderrを捨てるって 方法で正しいと思うよ >>658 xargs: basename: terminated by signal 13 って表示されちゃう >>661 俺は @ しか使ったことがない >>662 バッカこれはどっちも形容詞だよ だからネタとして成立してんじゃん コマンドの重複を数える(つまり$PATHに登録され実行可能なファイルの重複を列挙する)一番短い方法をってなんでしょう。ただし確実に動作しかつ可搬であることが条件です。 僕が考えたのは $ echo $PATH | tr ':' '\n' | xargs -I @ find @ \( -type f -a -perm -+x \) -exec basename \{\} \; | sort | uniq -c です。 basenameにしたらファイル名じゃなくなるじゃん >>668 ? もしかして-type fが見えてないだけ? パスの中に空白が入ってる or PATHの中身が多すぎたらおそらくアウト ( IFS=:; find $PATH -type f -a -perm -+x ) | sed 's|.*/||g' | sort | uniq -c ( IFS=:; for i in $PATH; do find "$i" -type f -a -perm -+x; done ) | sed 's|.*/||g' | sort | uniq -c できればsedでやりたいことなんですが aaa << bbb >> もしくは aaa <<bbb>> というようなファイルがあったとしてaaaの次行から「>>」を含む行までを読み出したいです。 (aaaが存在する行はすでに具体的な数値で判明しています; ここでは42行目とします) 特に二番目の場合には sed -n -e '43,/>>/p'とやると43行目に「>>」があるのに見つけてくれません。 なにか手助けをおねがいします。 >>671 sed -e '42,/>>/!d' -e 42d あれんじ sed -e '43,$!d;/>>/q' 言っても無駄なんだろうが、そんなファイルを処理しようってのが間違ってると思う >>676 処理したい時はどうすればいいですかね。 Windowsだと%TEMP%で一時ファイルの格納場所が指定できますがUnixではそういう環境変数はないのですか? $TEMPとか >>669 basenameって拡張子消えるよね? >>679 誰に対して使用して欲しいのか分からないな >>679 TMPDIR かな /tmp が古からあるからな。TMPDIRが設定されていたら(OSとして初期設定されているか、なんらかの形でTMPDIR設定されてたら)TMPDIR。設定されてなかったら普通に /tmp かな >>679 ,683 >TMPDIRが設定されていたら〜 って、TMPDIRを意識していればね。当然のように、んなの知らん、/tmp 一択ってのも普通にある >>681 一応、TMPDIRはPOSIX標準環境変数らしい。 tmpfile(3)やmkstem(3)なんぞはPOSIXとしてはTMPDIRを参照しろとはなってないらしい が、実装するならTMPDIR参照するだろうし、してる。ちゃんとtmpfile(3)やmkstem(3)やmktemp(1)などを使っていれば、誰でもなんだろうけどね >>685 それってPOSIX shellの話なの? 関数のなかってalias使えないっぽいね somefnc() { alias AAA='pwd' AAA } # → AAA: command not found 6.6 Aliases ... Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a compound command. As a consequence, aliases defined in a function are not available until after that function is executed. somefnc () { alias AAA='pwd'; } somefnc AAA otherfnc () { AAA; } otherfnc aaa="cat /etc/*release | grep -E '^NAME='" echo ${aaa} 上を実行すると、 $ ./hoge.sh cat /etc/os-release | grep -E '^NAME=' こうなってしまいます。 cat 〜の結果をaaaに入れる方法教えて欲しいです。 >>690 関数の中でaliasが使えないと言うよりか、 関数の定義が実行された時点でのalias情報が反映される。 つまりシェルスクリプトを上から実行していって、 somefunc()関数定義文が見つかると、 この関数 "全体" に対して、alias展開処理が行われてから関数が定義される だからsomefunc()関数定義文よりも前にaliasが設定されていれば そのとおりに展開されるが、関数の中はすでにalias展開済みなので もう処理されない ちなみにこれだとうまくいく somefnc() { alias AAA='pwd' eval AAA } evalは実行する段階でalias展開されるようだ evalで実行する文は、実行段階ならないと わからないのだから当然ではあるが >>695 すげぇ!ありがとう ちなみに今さっきの地震に伴う停電のせいでパソコンがおしゃかになったっぽいので 試せないw うちの研究室もすごいことになった。 ところで前々から関数のなかで alias が展開されないのを歯痒くおもっていたので eval 使えばいいというのは非常に有益な Tips だわ。 あまりにもPOSIX、POSIXしつこいから、POSIXでNGにしたったわ ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.0 2024/04/24 Walang Kapalit ★ | Donguri System Team 5ちゃんねる