シェルスクリプト総合 その34
■ このスレッドは過去ログ倉庫に格納されています
シェルスクリプトに関する総合スレッドです。 全般 ・荒しは無視しましょう。 ・丁寧な姿勢を心掛けましょう。 ・ネチケット(死語)を意識しましょう。 ・「○○(他の言語)でいいやん」は禁止。他のスレに行ってください。 シェルスクリプト総合 その33 https://mevius.5ch.net/test/read.cgi/tech/1584893550/ ・特記なき場合、POSIX準拠シェルが既定です(古きBourneシェルはほぼ絶滅しました) POSIX準拠シェルは(d)ash, bash, zsh, (m)ksh, yash, posh, (p)boshです 参考 https://unix.stackexchange.com/questions/145522/ 特定のシェルの専用機能に依存する場合は明示しましょう(特にPOSIX準拠シェルではないfish, (t)csh等) ・デフォルトシェルのシバンはBourneシェル時代からの伝統で#!/bin/shを使用します。ただしその実体はOSによって様々です Debian系 … dash CentOS系 … bash Alpine … ash(busybox) Android … mksh FreeBSD … ash Solaris,OpenBSD … ksh macOS … bash(Single UNIX Specification準拠のために一部動作が異なる) ・ログインシェルは/bin/shでない場合があります。例 macOS … zsh ・シェルスクリプトは可搬性を持たせるために可能な限りPOSIXに準拠しましょう 仕様 http://pubs.opengroup.org/onlinepubs/9699919799/ 参考 https://en.wikipedia.org/wiki/POSIX ・bash依存はなるべく避けましょう。自覚なきbashism。シバンが#!/bin/shなのにbashに依存する構文を使っていませんか? #!/bin/shを使うならシェル依存は厳禁です。bash依存するなら#!/bin/bashです ・BourneシェルはPOSIX標準化前に主にUNIXで使われていたシェルで多くの亜種が存在します Bourneシェル≒Version 7 UNIXのshに一番近いのはOpenSolaris由来のHeirloom Bourne Shell、次点でSchily Bourne Shellのoboshです Heirloom Bourne Shell: sh http://heirloom.sourceforge.net/sh.html Schily Bourne Shell: obosh http://schilytools.sourceforge.net/bosh.html 歴史的資料 https://www.in-ulm.de/ ~mascheck/ ・csh/tcshでのシェルスクリプトは*まったく推奨しません* 参考 http://www.speech-lab.org/ ~hiroki/csh-whynot.euc ・Linux/UNIXにはシェルスクリプトに便利な小さなコマンドがいろいろあります。Manページや各種リンクを見ましょう aproposやman -kでそれらしい単語による簡単な検索もできます ・ワイルドカード・パターンは正規表現ではありません。正規表現の話題はスレ違い(正規表現スレへ) ・シェル芸はシェルスクリプトとは異なります ・シェルスクリプトのことをシェルってゆうな こんにちは Macでzshを使っています フォルダに memo20200810.txt memo20200817.txt memo20200824.txt のようなファイルがあったとして(週に1つずつ増える) 最新のファイルをechoで見たいとしたらどうするのがいいでしょうか??? すみませんechoではなくcatでした 別にコマンドは何でもいいので最新のテキストの内容を見る方法を知りたいです cat "$(/bin/ls *.txt | tail -1)" ファイルが大量にあった場合には大きい順でソートして1行目を取り出すみたいに書いた方が速くないか? >>7 ちなみにlsだけフルパスなのは? lsがエイリアスである可能性を考慮とか? Unixで「複数のパスを区切る」といえばコロン区切りが普通だと思うんだけど (例えばPATH,CDPATH,NLSPATHとか) POSIXの https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03 でも言われている通りコロンが含まれているパスを指定することができない。 もちろん自分で勝手にPOSIXを逸脱して,バックスラッシュでコロンを エスケープする,みたいなことをしてもいいんだけど, なるたけ標準に合わせたい。 環境変数で複数のパスを指定するときに, コロンを含めるようにしているユーティリティの例とか知らない? PATHがコロン含められないのにコロン使えるようにするの? チャレンジャーだなw Unixはパスに使えない文字を決めたほうが良かったと思うね MS-DOS/Windowsはちょっと多すぎだけど >>13 Windowsだとドライブレターにコロンを使ってるから PATHの区切りは;ですね。 そういや環境変数名に記号とか改行とか使えるの知ってる?w コマンドの戻り値を反転させたい、つまり0のときは非0に、非0のときは0に変えたいんだけど、どうすれば? set -e状態のスクリプトで、特殊なコマンドを使いたい場合に。 また、makeで使うときにmake専用の記法(-とか@とか)がもしあればそれも。 >>17 if [ $? -eq 0 ]; then exit 1 else exit 0 fi すればいいと思うけど、困ってる点は何? >>18 set -e状態やmakefile中では、コマンドが終了した時点でエラー中断する。 なので、$?を参照するところまで至らない。 ちなみに、makefileの場合は、次のように一行にまとめてしまえばごまかすことはできるが、set -eスクリプトだとやっぱりダメなもよう。 コマンド; test $? -ne 0 >>19 まあ大体そうなんだろうとは思ったが どういうときにコマンドの戻り値を反転させたいのか気になるけど 処理をmain関数に入れてしまってこれでできるはず ( main ) && return 1 return 0 ! ( main ) でも一応良いはずなんだけど ifを使わずにコマンドの頭に !をつけるだけだと動かないシェルがあった気がする 今回はサブシェルが間に入っているから大丈夫かもだけど あとは、思いつきだけどこんな感じでも出来る気がする #!/bin/sh set -e trap 'exit $(($? == 0 ? 1 : 0))' EXIT 任意のコマンド set -eでエラー終了した時は$?が1とは限らん trap 'exit $(($? == 0 ? 1 : 0))' EXIT trap 'exit $(($? > 0 ? 0 : 1))' EXIT # 一文字短縮化w >>21 >>22 ありがとう。 コマンドラインの前に「!」を書けばいいのか。 manページを確認すると、「logical negation」とあったから、こちらの期待どおり。 trap 'exit $(($? > 0 ? 0 : 1))' EXIT これいいね >>28 あー、その記事読んだ。FSってフィールドセパレータだと思っちゃうよねw たまたま俺は、レコードとフィールドと、ファイル区切りもいるかなぁ?って 考えながら探してたから、お、4つもあるじゃんって感じで気付いたが >>17 シェルによって書き方違うかも知れないがこんな書き方も可能。 if コマンド [引数 ...] then exit 1 else exit 0 fi ほぼ >>18 と同じだが、コマンドを if の直後に書けばそのまま戻り値の判定ができる。 こういう書き方もできる。短い場合はこれでも良いかも。(でも後で見て分かり辛くなるかな?) コマンド [引数 ...] && exit 1 || exit 0 2行に分けても行ける。exit 1 の方が実行されたら下の行には行かないので。 こちらの方が見た目は分かり易いかな。 コマンド [引数 ...] && exit 1 exit 0 あ。上の方からゆっくり読んで下まで読まずに書いたら途中で色々と話が進んでいたorz https://mevius.5ch.net/test/read.cgi/tech/1537584801/206 この問題のレベル2を実装できないw レベル1は foo() { set -- $2 $1 $(shift 2; printf ' %s' "$@"); bar "$@"; } ↑こんな感じのが正解の一つだと思うんだが, これだと foo 1 2 3 4 '5 includes space' という引数を指定した場合に $ foo 1 2 3 4 '5 includes space' begin 2 1 3 4 5 includes space end というような出力になってしまう。 シェルスクリプトマスターしてしまったので シェルスクリプト難しいって言ってる人が 何が難しいのかわからなくなった シェルスクリプトが難しいんじゃなくて 初心者だから難しいだけなんじゃ? >>33 いくつかやり方があると思うがヒントな 位置パラメータが 1 2 3 4 5 の状態から 1 2 3 4 5 2 1 3 4 5 を作り出すことができれば あとはshift 5をするだけで 2 1 3 4 5 にすることができる この問題を解くカギは、終了条件をどうするか?なんだよ 1 2 3 4 5 2 1 3 4 5 を作り出すことができれば スペース入れたつもりだったが見やすくならなかったな 補足 わかると思うけど この問題を解くカギは、「ループの」終了条件をどうするか?なんだよ >>37 >>40 ちなみに >>33 の解答でも setやshiftを使ったりしてうまいこと 引数処理をしてはいる。 前スレ止まってると思ったら埋めずに分裂してたんかい >>44 できた。 結局 >>37 のヒントには全く頼らなかったわw eval "set -- \ $(printf "'%s' \\\\\n" "$2" "$1" shift 2; while [ $# -gt 1 ]; do printf '%s\n' "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; shift; done printf '%s\n' "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/")" 申し訳ないですが誰か教えてください。 以下のような感じで処理をしたいのですが a.sh(rootで実行) #!/bin/bash #処理 #postgresでb.shを実行 su - postgres -c "bash パス/b.sh `$1`" echo "b.sh完了" #testでc.shを実行 su - test -c "bash パス/c.sh" echo "c.sh完了" exit 0; b.sh(postgres) #!bin/bash #複数処理 exit 0; これをやるとb.shの一番最初の処理は実行できるのですが 2番目の処理からがエラーで実行できません。 postgresにてb.sh単体で処理を動かすと問題なく動かすことが出来るのですが・・・ このb.shをrootのa.shからpostgresのb.shを呼ぶ方法を教えていただけないでしょうか。 またシェルを分けずに、一貫してやった場合てどうやって途中でユーザ切り替えするのでしょうか? 申し訳ございませんが教えてください・・・ >>46 なんとなく "... `$1`"のあたりが原因なような気がする。 この状態だと引数の実行結果が空白区切りでb.shに渡される。 二番目の処理のエラーは見せられない感じ? >>47 [root@test-srv test]# ll 合計 8 -rwxrwxrwx 1 root root 216 9月 8 11:46 a.sh -rwxrwxrwx 1 postgres postgres 115 9月 8 11:44 b.sh [root@test-srv test]# cat a.sh #!/bin/bash echo "a.shの処理を開始します。" echo "a.shのテスト出力、引数は${1}です。" echo "b.shを実行します" su - postgres `/home/postgres/work/work/test/b.sh ${1}` echo "a.shを終了します" exit 0; [root@test-srv test]# cat b.sh #!/bin/bash echo "b.shの引数1は${1}です。" echo "b.shのテスト出力です。" echo "b.shのテスト出力です。" exit 0; [root@test-srv test]# ./a.sh test a.shの処理を開始します。 a.shのテスト出力、引数はtestです。 b.shを実行します -bash: b.shの引数1はtestです。: そのようなファイルやディレクトリはありません a.shを終了します [root@test-srv test]# 少し処理を変えた結果もこんな下の感じです。 [root@test-srv test]# ./a.sh test テスト: 引数1はtestです: command not found これはテストです `/home/postgres/work/work/test/b.sh ${1}` ` じゃん su - postgres b.shの引数1はtestです。 をしようとしてるじゃん、 -bash: b.shの引数1はtestです。: そのようなファイルやディレクトリはありません のメッセージの通りじゃん? いったい何がしたいんや? バッククォートは何のため? >>49 すみませんが、知識が貧しいので 詳しく教えてください・・・ >>50 単純にrootでa.shを叩いたら postgresユーザでb.shが引数付きで呼ばれるようにしたいて感じです。 その処理がいまいちうまく組めないのです・・・ ` で囲んだのは、その時点でそれが実行されてその出力に置き換えられる。$() はほぼ等価 su - postgres `/home/postgres/work/work/test/b.sh ${1}` は su - postgres b.shの引数1はtestです。 をやろうとしてる(b.shの引数1はtestです。に b.shのテスト出力です。もあるけどな) だから、 -bash: b.shの引数1はtestです。: そのようなファイルやディレクトリはありません というメッセージなんだよ su - postgres b.shの引数1はtestです。 をしようとしてるんだから やりたいのは、 su - postgres `/home/postgres/work/work/test/b.sh ${1}` ではなくて、 su - postgres "/home/postgres/work/work/test/b.sh ${1}" とかだろう >>53 出来ました! su - postgres -c "/home/postgres/work/work/test/b.sh ${1}" という形でした。 ありがとうございました! ちゃんとメッセージ見ような。だいたいにおいて正しく問題点が書いてあるんだからw >>54 参考までに聞いてみたいが、なぜバッククォートを使おうと思ったのか? クォートならどれでも同じだと思った? >>55 バッククォートの機能を知らずに使うと、エラーメッセージを見てもわからんやろな。 だが、typoでもよくみるメッセージだからな それを実行しようとしてる、なぜなんだぜぐらいから始めないとな cshやAndroidにも対応した上で POSIXに準拠したシェルスクリプトの書き方 (cshの場合はshで起動し直すらしい) https://togetter.com/li/1077808?page=5 ここまでやる意義は不明w >>59 それは読んだことがあるが どうももやもやしている どういう環境でそれが必要なのかが書いてないからかな? Androidといわれてもー バージョンわからないしー 検証環境用意できないしーw 読んでて思うのはtogetterはツイートをまとめるだけなので 検証内容をまとめるには適してねーわ >>60 同意。 「結局なにが分かったのか」 「解決方法はどうだったのか」 っていう,一番重要な情報もめちゃめちゃ みにくいところにある。 しかも,まとめ主は 「あとで記事にする」と言っているのに その後音沙汰がないし アカウント乗り換えてる模様。 怪しすぎる。 >>61 あとなんかシェルによってシバンの扱いが違うように書いてるように見えるんだけど シバンを解釈するのてOSだよね? 他の言語から起動することもあるんだから シェルはOSに丸投げしてるだけ だれかもうずばっと シバンはちゃんと書きましょう。 shを使うなら #!/bin/sh です。このパスがない環境はもうありません って言ってくれないかな?w 真偽不明でタイトルだけ残ってて困る >>63 ちょっとつっかかるけど 「このパスがない環境はもうありません」 これも根拠なくないか? POSIXでは定められていない訳だし, Solarisだと/bin/shはめちゃめちゃ古い。 POSIX shは/usr/xpg4/bin/shにある。 たまたまSolarisの環境があるから調べてみたが、 > Solarisだと/bin/shはめちゃめちゃ古い。 これはSolaris 10以前の話。古いというかPOSIX以前のBourne Shellだな。 これがSolaris 11ではkshに変わっている。 それからシステムの初期設定PATHと getconf PATHは必ずしも同じではない Solarisのシステムの初期設定PATHは/usr/sbin:/usr/binなんだが互換性上の理由(?)で POSIXに準拠してない可能性があるコマンドが入っていると考えて良さそう getconfはPOSIX準拠のコマンドということもあり、getconf PATHもPOSIX準拠のためのPATHを返す 例えば /usr/xpg4/bin/ が優先されている。ただし /usr/sbin は入ってない。Linuxでも/bin:/usr/binしか返ってこないな だから PATH=$(getconf PATH) なんかしてしまうと fdisk が使えなくなったw ちなみに /bin はどちらのPATHにも入ってない getconf PATHをどう使うかもOS依存な気がするけど、Togetterまとめに書いてあるように PATH="$(command -p getconf PATH):$PATH" こうすることで、標準を壊すことなくPOSIX準拠に近くするという意味になるんだろう ちなみにだけどcommandの-pオプションは古いzsh(たしか4.0あたり)で使えなかったりするけどなw 個人的にはgetconfがない環境があるのは知っていたので、そこは何も不思議はない Togetterまとめに書いてあるPATHの初期化方法は何をやりたいのかよくわからんw なんつーか一行にしようとして意味不明にしてるとしか思えんな。こんなんでいいやろ? default_path="$(command -p getconf PATH 2>/dev/null ||:)" [ "$default_path" ] && PATH="$default_path:$PATH" ↑でこれはシバンがないほうがいいかどうかとは全く関係ない話 まぜるなや >>67 Solarisで $ getconf PATH するとPOSIX準拠の命令が優先されるような 設定が返ってくるのな 勉強になった。さんきゅ〜 >>67 ちなみに棘でもzshのcommand -p非対応については 触れられてたな。 あきらめてhash使ってたが。 >>71 command -p (もしくはただのcommand)って シェルで微妙に動作が違うんだよな ちゃんとまとめてないからきっちりとは言えんけど 例えば dash や ksh だと command -p printf --help は ビルトインコマンドが実行される zshだと外部コマンド zsh で command -p : がエラーになるのはそのせい なんで昔の人って command ・・・ 外部コマンドを実行する builtin ・・・ ビルトインコマンドを実行する function ・・・ シェル関数を実行する みたいに全部作ろうと思わなかったんだろうな? ユースケースを思いつかなかったということなんかな zsh % type command command is a shell builtin https://linux.die.net/man/1/zshbuiltins command [ -pvV ] simple command The simple command argument is taken as an external command instead of a function or builtin and is executed. If the POSIX_BUILTINS option is set, builtins will also be executed but certain special properties of them are suppressed. The -p flag causes a default path to be searched instead of that in $path. With the -v flag, command is similar to whence and with -V, it is equivalent to whence -v. See also the section 'Precommand Modifiers'. zshにしたら今まで使ってたbashがぶっ壊れることって結構ありますか? bashがぶっ壊れるって何? 両方入れるだけでしょ? bash消してzsh入れるっていうのならぶっ壊れる可能性が高い もしくは最初からbashが入ってない(必要ない)システムを使うとかね 例えばdashを採用しているdebian/ubuntuとか そういうシステムでもdashを消してzshにしたらぶっ壊れる zshをPOSIXモードで実行すればワンチャンあるかもしれんけど bash用スクリプトはzshで動かすことを考えて作っていない限り動かない sh用スクリプトであってもzshでは動かない可能性がある カタリナにしてからmacのターミナル起動するたびに出てくる `chsh -s /bin/zsh`って実行したらbash使えなくなるよね? >>78 ならない。 macは最初からbashとzshの両方がインストールされてる。容量の無駄遣い macOSでbashは/bin/shとして使われてる。Debianで使われてるdashよりも重い 最新のbashは5系だがmacOSの/bin/shのbashは3系で古い。 ライセンスのせいで新しいbashにアップデートできない しかしPOSIX準拠のdashにしようと思っても bash依存してるスクリプトがあって互換性の問題が出るからdashにもできない つまりmacOSの/bin/shはPOSIX準拠のdashでもない上に 古いbashという中途半端な状態 >>77 > `chsh -s /bin/zsh`って実行したらbash使えなくなるよね? さっさと実行しろ。どうせシェルスクリプトの類はbashで動く 明示的にしていて無い限りmacOSでは古いbashでスクリプトを動かすことになる 最新のbashの機能も使えない >>81 コマンドについてはデフォルトのシェル用に作られたものを、別のシェルでは動きを変えて対応している。 だから同じ名前のシェルでもOSが異なれば、動きが変わる。 Linuxのshはbashがsh風に動かしているだけで、いろんなところがshではない。 >>84 それが>>73 のレスと何の関係があるの? どういうつながりでそういう発言をしたのかを聞いてる >>85 UNIXは体系的に作られたものではないということ。 それぞれが他人によって作られ、それらを統合しているため、一貫性がないのが普通。 細かい規格も存在していないので、コマンド、スクリプトは環境に依存する。 >>87 >>73 後出しの思い付きを垂れたからやろ。 >>86 って言ってるのに、疑問に思うな思考停止とか読めないやつなんだろな そもそもUNIXは、たまたまこうなったんじゃなくて、合理的な理由があるんだけど、なんでそれを知ろうとしないの? UNIXはこういう思想で物を作ってますと宣言してるのに。 いやー、たまたまやろ。 根っこにポリシーはあっても、それ以外はかなりのいきあたりばったり。 >>92 それな。"$1" みたいにダブルクォートでくくらないといけないのは 明らかな仕様の失敗だって言われてるし 他の言語と同じようにPOSIXシェル以前やBourneシェル以前との 互換性をある程度保ちながら改良を続けてきた 必ずしも合理的な理由があるわけじゃないよ。作者がたまたま必要だった 必要と思いつかなかった。それだけだろ >>93 二重引用符で囲まない場合, どういう利点があるの? あとそれを説明した記事とかある? >>94 > 二重引用符で囲まない場合, > どういう利点があるの? ない やりたいことはevalで代用できる >>94 あるというか、そうしなければならない場合も。 引数がなかったときに、クォートされてると、空文字列になってしまうので、意味が変わる。 make $1 みたいなスクリプトだとクォートしてはいけない。 >>98 試してから言え。 簡単に嘘呼ばわりすんな。 >>97 だと makeに "install uninstall" という文字を渡すと エラーにならずに実行できるから駄目 試さなくてもわかる ■ このスレッドは過去ログ倉庫に格納されています
read.cgi ver 07.5.5 2024/06/08 Walang Kapalit ★ | Donguri System Team 5ちゃんねる