シェルスクリプト総合 その28
■ このスレッドは過去ログ倉庫に格納されています
シェルスクリプトに関する総合スレッドです。 全般 ・荒しは無視しましょう。 ・丁寧な姿勢を心掛けましょう。 ・ネチケット(死語)を意識しましょう。 前スレ: シェルスクリプト総合 その27(https://mevius.5ch.net/test/read.cgi/unix/1525337663/ ) お約束 ・特記なき場合、Bourne ShellもしくはPOSIX準拠のsh可換シェルが既定です つまりシバンは#! /bin/shです。 他のシェル(bash, zsh, ksh, (d)ash, yash, posh, fish, (t)csh)などの専用機能に依存する場合は明示しましょう。 OS X, GNU/Linuxユーザーは/bin/shの実体がbashなので*特に*注意(自覚なきbashism---シバンが#!/bin/shなのにbashに依存する構文を使っていませんか?)。 ただしDebian, Ubuntuなどでは/bin/shの実体はPOSIX sh互換のdashですのであまり気にしないでも大丈夫です。 FreeBSDユーザーは/bin/shの実体がashなので注意。 Solaris, OpenBSDユーザーは/bin/shの実体がkshなので注意。 csh/tcshでのシェルスクリプトは*まったく推奨しません*。 (参考URL: http://www.speech-lab.org/ ~hiroki/csh-whynot.euc) ・POSIXに準拠しましょう 有用なリンクはhttps://en.wikipedia.org/wiki/POSIX にまとめられています。 最新の仕様はこちらへ: http://pubs.opengroup.org/onlinepubs/9699919799/ (左上の「Shell & Utilities」から各コマンドやファイルの仕様を参照することができます) ・Version 7 UNIXのsh(1)に一番近いのはOpenSolaris由来のHeirloom sh、次点でDebianなどに搭載されているDash shell Heirloom sh: http://heirloom.sourceforge.net/sh.html Dash shell: http://gondor.apana.org.au/ ~herbert/dash/ ・UNIXにはシェルスクリプトに便利な小さなコマンドがいろいろあります Manページや各種リンク(http://pubs.opengroup.org/onlinepubs/9699919799/idx/utilities.html など)を見ましょう。 apropos(1)ないしはman(1)の-kオプションでそれらしい単語による簡単な検索もできます。 ・ワイルドカードは正規表現ではありません。 正規表現の話題はスレ違い(正規表現スレへ) ・シェルスクリプトのことをシェルってゆうな 初心者へのアドバイス ・適した道具を判断するのも頭の重要な使い方。 シェルスクリプトよりもPerlまたはPythonの方が適した処理にはそちらを使いましょう。 ・知らないコマンドが出てきたらman(1)を引きましょう。 ・思い通りに動かないときは、まずはsh(1)の-xオプションでトレースしましょう。 回答者への注意事項 ・相手がシェルスクリプトでの処理方法を質問しているのにもかかわらず、よく知りもせずに、「そういうのはPerl, Python使え」と回答するのはやめましょう。 安易にPerlやPythonに逃げずに小さなコマンドを組み合わせシェルスクリプトで処理するのが頭のいいやり方。 ・質問に対して問題が間違ってるといちゃもんをつけるのも避けましょう。 補足(今スレ限り) いままではUNIX板でスレ立てをしていましたが、IDやワッチョイが無いことを理由とした自演・荒しが目立ってきておりスレがまともに機能しなくなりかけているので、今回よりプログラム板に移動することになりました。 テンプレの↓ > 自覚なきbashism---シバンが#!/bin/shなのにbashに依存する構文を使っていませんか? ちょっと痛いかな bashのあれやこれは、shでどうかけばいいかってのがあるといいな 特に配列。配列は引数として処理するんだよっていうのが体系的にまとまるといいな 本来の引数配列以外で配列が必要になったら、基本的にはawkその他の言語使うなあ。 まあ自分用だと気の迷いで引数配列転用したり、evalで頑張ったりすることもあるけど、 仕事で他の人に引き継ぐこと考えると保守性で問題になるので 仕事ではやらないようにしてる。 >>16 配列が必要になるっていうのは、シェルスクリプトの プログラミングの仕方が間違えてるってことだよ 例えて言うならば、関数型言語をオブジェクト指向言語的に 使って、オブジェクト使いたい。インスタンス作りたい メソッド作りたい、継承、カプセル化欲しい!って言うようなもん シェルスクリプトは引数または標準入力で渡されたものを 順次処理していくという書き方をするもんなんだろう。 一時的に変数にためておくなんてことはしないので 配列は使わないですむ evalだけは怖いから使わない それでしか解決できない問題をまだ扱った事がないからだが evalは信頼できない文字をevalするのが問題なのであって コードの中で作り出した文字をevalするなら問題もないんだよ 信頼できない文字であっても、使える文字種を限定すればいいし >>17 だいたい同感。 配列や連想配列がぴったりした応用もあるんだけど、 シェルで書くようなプログラムじゃないね。 あるリモートファイルの最新のバージョンをダウンロードする方法ってありますか。 具体的にはhttp://ftp.jaist.ac.jp/pub/GNU/gnuzilla/ ←ここの最新バージョンのディレクトリを知りたいです。 >>17 バッチファイル程度のシェルスクリプトしか 知らないからそう思うんでしょ 知れば奥が深いよ 配列も結構多くのシェルがサポートしてる(例えばcsh) 極端な例を挙げるなら、行列演算をシェルスクリプトで書く馬鹿はいないみたいな話よ。 配列に限った話ではなく、ほとんど全ての高級言語にあるのにシェルにはない言語機能がいろいろある。 例えば構造体に類する機能とか、参照とか。 言語には向き不向きがあって、シェルでは向いてない用途に無理矢理使うのは井の中の蛙。 >>21 wget -qO - 'http://ftp.jaist.ac.jp/pub/GNU/gnuzilla/?C=M ;O=D' | grep -m1 -Po '\[DIR\].+?href="\K[0-9.]+' >>25 ありがとうございます。 上手くいきました。 できるだけ可搬なUNIX時間<->ISO 8601形式時間の変換器ってどんなのがありますかね PerlやRubyでいけるのは知っていますが,もうちょっとPOSIXに従ってるやり方が知りたいです >>28 GNU date 限定の技だと思うのであまり嬉しくないかもしれないけど、以下のコマンドでいけるそうだ。 date --date "@1501201492" +"%Y%m%dT%H%M%SZ" ちなみに出典は以下の記事です。 dateコマンドを使ってUnixTimeをISO8601形式へ変換する - Qiita https://qiita.com/EDAPIYO/items/1bf163604006fd667988 >>28 小出しでスマン。こういうオプションもあるみたいなんだけど、man に記載されてないね…… $ date --date "@1501201492" --iso-8601=seconds 2017-07-28T09:24:52+0900 >>29 ありがとうございます。 そうっすね……。dateコマンドでできるのは知っていますが, これ,ものっすごく可搬性悪いんですよ。下手するとPerlでやったほうがマシなくらい。 なぜかというと,各種OSによってdateコマンドのUNIX時間<->ISO 8601形式時間変換のオプションが違うんです。 *BSDはdate -jだしGNU/Linuxでは書いてもらったdate --dateとかdate -dとか。 ある種のOSではdate -rもあるっぽいです……。 >>31 BSD一般は date -r 数字 では? 最近の NetBSD だと date -d @数字 とも書けるから GNU と共通にできる。 >>28 アルゴリズムとしては 秒数→日時変換と日時の足し算だけなんだから 自力で作れない? 自力で作るならコマンド見分けたほうが楽だなw どちらも同じオプションだけど意味が違うものを使って見分ける >>31 自分で言ってる様に Perl で書いちゃえば良いような… 自分で足し算すると、サマータイム・うるう秒は、大丈夫なのか? bashです。 aa.shからbb.shを呼び出した時、リダイレクトでログを残したいんだけどうまく行かない。 なにか間違っているでしょうか? (aa.sh) #!/bin/bash b_shell="./bb.sh" cmd="${b_shell} > ./bb.log" ${cmd} (bb.sh) #!/bin/bash echo "hoge" bb.sh で echo "$*" してみりゃ、理解できるだろう >>36 確かそういうのを考慮してないんじゃないの? >>45 うるう秒はUNIX Epochからの秒数には入ってないので無視して良い。 サマータイムやタイムゾーンの考慮は必要だしとても大変。 日本のタイムゾーン固定ならサマータイムも今のところ無視できるし簡単だけど、perlでも使ってライブラリに任せた方がいいと思う。 >>47 この板のIDは8桁だから9桁目(いわゆる末尾)はないよ 昨日発見した事なんだけど、シェルというよりは test コマンドの問題だけど、test -f で対象のファイルを指定しない時って0になるのな。 これはbash用に書かれたこんな記述の時の動作によって気づいた。 if [ ! -f $file ]; then echo ファイルなし exit 1 fi これで file に何も代入されてないとか空文字列の時にファイルなしにならない。 なんだこの動作は? testコマンドの仕様?たまたま俺の使ってたLinuxのtestコマンドのバグ? まあbashなら[[ ]] 使っとけばエラーになるからそっち使っておけば良いんだけどね。 find [xxx] -type f | xargs grep [yyy] をよく使うので search xxx yyy みたいな alias にしたいんですけど 引数を途中に挟むのってどうすればいいんでしょうか >>50 そ、そうか。 しかしなんでまたそんな仕様になったんだ? >>51 bash の場合は function で定義するしかないようだよ。ググって探してみな。 >>53 functionでできました ありがとうございました >>52 ググって探してみなw まあ、何らかの歴史的な背景だろうけど、明確に仕様に書いてあるのだから >>51 は grep -r [yyy] [xxx] もしくは rgrep [yyy] [xxx] でいいんじゃねーの? 俺はagの方をよく使うが >>49 そもそも if [ ! -f $file ]; then だと $fileにスペース等が入っている時に 対応できないので "$file" と書くのは必須 更に言うなら set -u すればいい。$file が空のときはエラーになる 0 arguments: Exit false (1). 1 argument: Exit true (0) if $1 is not null; otherwise, exit false. って、だけなんだけどな。2 arguments から本格的に判定に入るってとこかな。演算子が有効なものなのかも含めて ああ、1 argumentの場合は空文字列でないかどうかっていうテストになるのか。そう思えば不思議でも何でもないな。使わないけどw 歴史的にそんなのがあっても不思議ではないな、実際どうなのか知らんけど # set variable identifying the chroot you work in (used in the prompt below) if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then debian_chroot=$(cat /etc/debian_chroot) fi これは、~/.bashrc だけど、 変数中に空白があっても良いように、たいてい変数は" " で囲む。 一方、固定値は囲まない こういうように、シェルスクリプトには様々な引掛けあるから、危険! Ruby などのプログラミング言語を勧める マンガの「Linux シス管系女子3」に書いてあるけど、 「rm -f $file」と変数を" " で囲わなかったので、 「; rm -rf shared」というファイルがあったため、 rm -f ; rm -rf shared と文がつながって、共有フォルダのファイルがすべて消えてしまったとかw >>60 変数の値として ; が入ってても、文の区切り扱いにはならないので嘘くさい。 evalしてれば話は別だが。 だから、ディレクトリパス・ユーザー名とか、 システムで使うものに、半角空白を入れたら、ダメ シェルスクリプトを書いている奴が、変数を" " で囲んでいないと、バグるから パスに半角空白を入れないようにって、 Mac ユーザーにも、よく言われる Windows には、半角空白が入ったフォルダ名があるから、危険 >60 は、 日経Linux で連載している、Piro の有名なマンガ、 「Linux シス管系女子3」に書いてある このマンガを知らなかったら、もぐりw >>62 > Mac ユーザーにも、よく言われる ほんとかよ。macOSの標準ディレクトリに普通にスペース入っているのあるけど うそくさい、えせくさいw 実際に試してみればええんじゃないの $ mkdir shared $ touch '; rm -rf' $ ls '; rm -rf shared' shared/ $ for file in *;do rm -f $file; done $ ls '; rm -rf shared' $ mkdir shared $ ls '; rm -rf shared' shared/ $ for file in *;do rm -f "$file"; done rm: cannot remove 'shared': Is a directory $ ls shared/ >>60 >一方、固定値は囲まない 囲まなくていいのは囲まないだな。当然囲む必要があるのなら囲む " でなく ' ででも 囲まないで ¥ 使う物好きもいるかもしれないが 変数でも囲まないのは同じように囲まなくてもっていうところが原点だろうが、世の中そんな条件付けが許されるほどそう甘くはないっていう その下の rm はイミフだな。>>61 のほぼ言う通り。もちょっと「なんか」あるんじゃないの そんなマンガ知らなかったらモグリよばわりされるならモグリで十分すぎるw 日経Linux は、Ruby の作者・Matz と、マンガの「シス管系女子」の連載が有名 「シス管系女子」も、もう3冊目。 たいてい本屋の目立つ所に置いてある システム管理者の必須本 >>67 それ以上システム管理者とやらを貶めるなよw >>60 > 変数中に空白があっても良いように、たいてい変数は" " で囲む。 > 一方、固定値は囲まない 固定値だから囲まないんじゃなくて、 固定値かつ、空白が含まれてないから囲ってないだけ 固定値でも、空白が含まれていれば囲む >>62 > Windows には、半角空白が入ったフォルダ名があるから、危険 危険なんじゃなくて、半角空白が入ったフォルダがあるから きちんと対応せざるを得なくなってバグが減る 下手に半角空白がないシステムばかり使っていたら 半角空白に対応するのを忘れてしまう。そっちのほうが危険。 そんなことよりもshellcheckでチェックするほうが常識 これを知らないやつはモーグリ >>60 > こういうように、シェルスクリプトには様々な引掛けあるから、危険! > Ruby などのプログラミング言語を勧める Rubyは外部コマンドを呼び出すのがすごく面倒くさい言語だが 参考 https://qiita.com/zakuroishikuro/items/3ab4476ff53f50a163be > _人人人人人人_ > > めんどい < >  ̄Y^Y^Y^Y^Y ̄ ``を使えば、最小2文字の追加で呼び出すことができる (正確にはputsしないと標準出力は表示されないからスペース入れて7文字) Rubyでも結局同じ問題が起きる # ruby −e Vdir=Wa bW; `ls −al #{dir}`V ls: cannot access VaV: No such file or directory ls: cannot access VbV: No such file or directory (全角なのは書き込みエラーが出たから。もう面倒だから一律変換する) 回避策はいちいち言わなくていい、そんなのあるの知ってる。 Rubyでも様々な引掛けがあるという話。 Ruby使うくらいならPython使うわ。 でもここはシェルスクリプトのスレなんですよ。 しかも「ダブルクォートで囲む」という至極単純な対策でバグを回避できるんだから 他の言語を使う必要もないな。 ところでファイル名にダブルクォートが入っている場合があるので ダブルクォートで囲む前にsed -e 's/"/\\&/g' とやる必要がある。 >>73 > ところでファイル名にダブルクォートが入っている場合があるので なんのことでしょう? ファイル名にダブルクォートが入っていても なんの問題もないですよね? [test.sh] #!/bin/sh n="a'\"b" file "$n" $ echo test > a\'\"b $ ./test.sh a'"b: ASCII text bashでは0(真)か0以外(偽)となりますが、 Cスタイルのforループの条件判定のところにi==0( i>=0とするところを間違えてやってしまいました) とやったら一回もループに入らなかったのですが、 Cスタイルのforループの中では0は偽なのでしょうか? forループは下のループです。(chinachu-mirakurun-sleep scriptでスキップした番組の時に録画動作に入らない ように改造したものです) for ((i = ((${#IsSkipArray[@]} -1)) ; i>=0 ; i--)); do if [ "${IsSkipArray[i]}" = "false" ]; then (( RecTime = ${RecTimeMsec[i]} / 1000 )) elif [ "${IsSkipArray[i]}" = "true" ]; then : fi done ちょっと訂正します。 間違えたコードはi==0ではなく、条件式にi=0としていました。 だから毎回判定するごとにiに0が代入されてループに入れなかったんですが、 お聞きしたかったのは0が偽だからループに入れなかったのか?ということでした。 条件式に代入式を持ってくる時点で素人丸出しなんですが、どうかご容赦ください。 ttps://mywiki.wooledge.org/ArithmeticExpression ここのサイトを見たらfor (( ; ; ))もArithemetic Expression(算術式)で、 However, when evaluating an arithmetic expression, C language rules (0 is false, anything else is true) apply. と書いてありました。Cの原則が適用されるようですね。自己解決したっぽいです。 どうもお騒がせしました。 >>72 エディタに、TeraPad を使っているけど、半角・全角文字へ変換ってある こりゃ便利 >ファイル名に、ダブルクォートが入っている ファイル名に、! が入っていたら、どうなる? テレビ番組の録画で「何々!」「何々!!」みたいな番組名が、ファイル名になる場合 >>80 感嘆符が特殊文字になるのってインタラクティブなときだけじゃね? >>80 こういうのって何言いたいのかわからんな。「問題」でも出してるのか?? いやだから,感嘆符が特殊文字になるのはインタラクティブなときのみじゃないの? ! を特別扱いするシェルを使っているかどうかの問題ではないかな。 OSやファイルシステムが特別扱いしているってこともあり得なくもないが。 UNIX系OSだと / しか特別扱いしてないよね。 Linux 板で、テレビ番組名に、!!! みたいなものが入っていて、 それがファイル名になる場合に、バグるって言ってたから ! は、エスケープも出来ないとか 他の環境ではNGの可能性があるということを考えると 怪しい文字は極力ファイル名に使わないようにしてる。 ある意味思考停止だけどw CD-R焼く時、使える文字がフォーマットによりまちまちで、 ものすごい複雑だったことを思い出したわ。 ロミオとジョリエットwとか、ロックリッジとかあったなぁ。 >>87 だから、なにを言っているのかわからんてww >>80 ばーかばーかさんへ file "$1" が文字が原因で失敗する事例なんてありません エスケープが必要な場合など存在しません。 わかったら黙って帰れ >>86 特殊文字って場合によりでしかないよな。特殊文字だから必ずバグる(?)なんてないし シェルというか引数として受けたコマンドがって感じかなあ。bash自体がスクリプト中の引数を含めたコマンド文字列に特殊文字があっても何もしない/しないようにできるし ID:htH9kPBSが言っているのは、ID:TC+4ZTQW が言っているコマンドラインでのヒストリ(参照)処理に引っかかる場合で、シェル「スクリプト」の問題ではないだろう スクリプトとして關係ないが、超シンプルにしての $ ls '!' ls: !: No such file or directory まあ、そうだな $ ls ! ls: !: No such file or directory ほう $ ls "!" -bash: !: event not found なんでやねんっ!ってとこではあるな コマンドラインでも ! を打たなきゃファイル名がんなの入っていてもだし、! を含めたファイル名なんぞを打たなきゃでも ' でくくればいいんじゃね だからすごく単純な話で $ cat ./hstex.sh #! /usr/bin/env bash case $- in (*H*) echo enabled ;; (*) echo disabled ;; esac $ ./hstex.sh disabled こういうことでしょ? つまりシェルスクリプトになってるときbashはヒストリ展開を既定で行わないから エスケープを考える必要はないの。 はぁ、シェル「スクリプト」かどうかの問題じゃないんだってば >>93 はevent not foundってでなかったんで 考えるのメンドーなんでぐぐって https://qiita.com/Qutjl/items/6e0056853c94011d245b のネタパクるけどさ $ ruby -e "puts 'Hello, World!'" -bash: !': event not found はい、たしかにそうなりますね。 テキストエディタで「puts 'Hello, World!'」という内容のcode.txtファイルを作成する $ cat code.txt puts 'Hello, World!' $ a=$(cat code.txt) $ echo "$a" puts 'Hello, World!' 変数 a に先程の文字列を入れました $ ruby -e "$a" Hello, World! はい、エラーになりません。 これはシェルスクリプトが特殊なんじゃねーよ。 C言語で、ソースコードに"\n"と書いてある文字列をprintfした時と \nという文字列が入っている変数をprintfした時の挙動と一緒だろうが プログラミングの基礎やで。ソースコードをパースするときの処理かどうかの違い >>95 なにを言っているのかわからんww なんか面倒なことになってるなww 「スクリプト」の問題じゃないよ、ソースコードをパースとかよくわからん例えに見えるけど、単に「コマンドライン」でのパース(?)の問題。それもヒストリ展開だけの。>>94 でID:TC+4ZTQWが再び言及しているような ああ、bashのバージョンで $ ls "!" -bash: !: event not found にならんことあるな。新しいのは。 $ ls "!!" でもいいよ。今度は $ ls !! の挙動が変わるけど。まあ '' でくくればいいだけ&単に「コマンドライン」での問題 えぇ…… もしかして俺が間違ってるのかと不安になるほど自信満々な書き方だなw >>96 >'' でくくればいいだけ うっかり ' を二つ並べた。シングルクォーテーション >>97 まあ...気にすんな。「ナニ」か違う(?)と思ったのだろう。よくわからんけど 若者はヒストリー置換を知らないんだなあと感じいる俺はオッサン。 ヒストリー置換は対話的な入力にしか影響しないから、 スクリプトにするときは気にしなくていいよ。 >>97 そりゃ間違ってるでしょ。 シェルスクリプトになってるときは〜とか言っちゃってる インタラクティブシェル上でも変数を""で囲えば同じなのに ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.4 2024/05/19 Walang Kapalit ★ | Donguri System Team 5ちゃんねる