シェルスクリプト総合 その26 [無断転載禁止]©2ch.net
■ このスレッドは過去ログ倉庫に格納されています
シェルスクリプトの総合スレです。
□お約束
・特記なき場合はBourne Shell(/bin/sh)がデフォルトです。
bash/zsh/ksh/ashなどに依存する場合は明示しましょう。
Linuxユーザは/bin/shの正体がbashまたはdashなので特に注意。
FreeBSDユーザは/bin/shの正体がashなので注意。
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に逃げずにシェルスクリプトで処理するのが頭のいいやり方。
前スレ
シェルスクリプト総合 その25
http://echo.2ch.net/test/read.cgi/unix/1439563321/ >>628
foo() echo foo ならzshは理解するぞ
Bourne shell系はfoo() any-compound-commandだけ理解して
foo() any-commandは理解しない >>628
POSIX準拠なのかはよくわかりませんが、sh ならば
foo()
foo1() echo "$1"
で、sh でも bash でも動かすなら
foo() { :; }
foo1() { echo "$1"; }
でいけると思います。 訂正
× foo(): echo foo
○ foo() echo foo
>>629
ごめん、zshでは試していなかった。
今zsh入れてたマシン壊れてるんだったw
別の環境にzsh入れたらたしかに動いたね
dash、ash、zsh では動いた
bashだけ動かなかった func() :
↑これ確かにシェバンを #! /bin/sh にすると通るけど
#! /usr/bin/env bash にすると撥ねられるな。
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_05
一応現行POSIXのシェル関数の定義はここに書いてあるけど、func() : は恐らく適合してるな。
多分bashの文法チェックが少しばかりおせっかいすぎるんだろう。
でもPOSIXに準拠した記法なのにエラーになるって嫌だなぁ あれ。yashでも「syntax error: a function body must be a compound command」って撥ねられるわ。
yashはかなり厳密にPOSIXに従ってる筈なんだけど… またちょうどよくシバンの話がw
シェルスクリプトのファイルを直接実行するのではなく
ライブラリとして他のスクリプトから読み込むだけの場合
シバンいらないよね? またその場合の拡張子ってどうしてる?
シンタックスハイライトとか自動で適用させたいんだけど
余計なものはできるだけ排除したい >>632
そうなんだよね。あと
> fname ( ) compound-command [io-redirect ...]
ここにio-redirectが書けたのがびっくり
どういう時に使えば良いんだろうか? >>635
俺は未インストールコマンドの処理に使ってる
check_command()
{
type "$1" > /dev/null 2>&1 || {
printf '%s%s\n' \
"$1" 'をインストールして下さい。'
exit 1
} >&2
return $?
}
check_command "curl"
こうするとエラーメッセージが標準出力に出ないからパイプ繋いでてもおかしなことになりにくい。
もっとも |& ←みたいなパイプなら無理だけど、そもそも標準エラー出力ごとパイプに使うなんて稀だろうという推測。 >>636
いや、そうではなくて関数定義の最後に書けるんだなーって話
その例で言えば、こういう書き方ってこと
(関数全体を別の所に出力したいときには便利か、うーん使うかな?)
check_command()
{
type "$1" > /dev/null 2>&1 || {
printf '%sをインストールして下さい。\n' "$1"
exit 1
}
} >&2
# ついでにどうでもいい所を俺の趣味で変更したw
# exitもreturnに変えたい。もしくはexit_if_command_not_foundみたいな関数名にしたい
> もっとも |& ←みたいなパイプなら無理だけど、そもそも標準エラー出力ごとパイプに使うなんて稀だろうという推測。
/dev/ttyに出力するって手もあるよ
$ (echo stdout-a; echo stderr-a >&2; echo tty-a >/dev/tty) |& sed 's/a/b/'
tty-a
stdout-b
stderr-b >>633
yash覚えた
最低限機能しかももってないと思われるashで動けば
Bourne Shell 系全部でうごくだろうと思っていたから
そういうこともあるんだなーって思った >>637
あ,そういうことか。勘違いしてたわw それはちょっと使い道思い付かん
ところで,
return $?
↑これ消した理由なに? C言語をやってるせいか関数が終了ステータスを返さないと不安で仕方無いんだけども,
シェルスクリプトだと要らないのかな。今まで特にreturn行のせいで不具合が発生したこともなかったけれど。 >>639
何も書いてなければ、その時点の$?がそのまま返るだろ?
> C言語をやってるせいか関数が終了ステータスを返さないと不安で仕方無いんだけども,
Ruby(最後に評価された値を返す)やってるとreturnなんて書かないんだがw > 今まで特にreturn行のせいで不具合が発生したこともなかったけれど。
まあ不具合は発生しないだろうね。
関数の最後でなにもしないなら、その時点の $? が返る所を
$?を取り出してreturnの引数で$?を返すようにしただけだからw Cとか化石みたいな言語使ってる人って無駄が多いよね
生産性悪w そういうこと言う奴って大体Cが出来ないコンプレックス抱えてるんだよなぁ Cはハードに近いところを表現するのに適してるから絶対に無くならないけどね。
化石だけど生活には欠かせない燃料みたいなもんかな。
Cで書かれてるシェルのスレで否定しちゃうのはちょっと痛いね。 俺はCを否定してないぞ。C以外を使っていれば
returnしなくていい言語だってあることを知るだろうし、
returnを書くことに拘る必要はないってことだ >>640
ruby使ってたってreturn書くことあるだろ
implicitとexplicitを状況に応じて使い分けることが大事 >>649
Cに対するコンプレックス同様
Rubyに対するコンプレックスも見苦しい >>647
returnを省略できる場合に、returnを書くことはないよ 話をぶった切ってすまんが、なるべく依存関係が少ない・かつインタラクティブがある程度機能的なPOSIX互換シェルって何があるかな。
ksh98とかyashとかは使ったことあるけど、ああいう感じでもうちょい開発が盛んなやつとか知らない? 連続足し算だけど
#!/bin/bash
while true
do
add=expr 0
read x
add=$(($add + $x))
echo '----------'
echo ' ' $add
done
でできるが
line 5: 0: command not found
1
----------
1
./tasizan-renzoku.bat: line 5: 0: command not found
2
----------
3
./tasizan-renzoku.bat: line 5: 0: command not found
と 気色悪い。 どうすれば正解なの? すごいね、以下でエラーがなくなった。
#!/bin/bash
while true
do
add=(expr 0)
read x
add=$(($add + $x))
echo '----------'
echo ' ' $add
done a=0
while read x; do
a=$((a + x))
echo ----------
echo ' ' $a
done ありがとう。 合計を外側で定義することがわからなかった。
こうなると 電卓より便利! また頭がおかしいのがわいてきた
UNIX板荒らさないでほしい コマンドにローマ字で名前付けるやつは多かれ少なかれキチ成分が入ってる (経験からくる偏見) for i in `seq -f %02g 1 10`
do
echo ${i}
done
このfor文の中で01の時に1、02の時に2を指定する方法ってありませんか?
簡潔に言うと先頭の0を取ったものを使いたいです >>662
どうしてデバッグが楽なのか知らんが、
少なくともprintfデバッグすると
終了コードが0に変わるからデバッグしづらいな >>664
2桁と決まっているなら
echo "${i##0}"
逆にしてこっちのほうが楽だと思うが
for i in $(seq 1 10)
do
printf "%02d\n" $i
done 拡張POSIX準拠らしいけど
echo {1..10} {01..10}
arch linuxだと普通のshでも展開してくれる >>668
> arch linuxだと普通のshでも展開してくれる
してくれなかったぞ > 拡張POSIX準拠らしいけど
拡張POSIXってしう仕様があるの?
POSIXを拡張したもので、POSIX非準拠って意味だと思ってたんだけど? シェルスクリプトってさ、POSIXが標準だけど
実質bashがデファクトスタンダードって考えて良いのかな?
zshやfishを使っていてもbashはインストールされていると考えていい?
いや、あるコマンド作ろうと思ってるんだけど、
基本はPOSIXで動くようにするけど、bashがインストールされていれば
拡張機能が使えるようになる。という仕組みは
ほぼすべての人が拡張機能使えると考えて良いんだろうかなって思って >>673
bash前提はだめ。
BSD系にはデフォルトで入っていないし、後から入れてもパスが違う。 >>671
ごめん
/bin/shのバージョン確認してみたら
GNU bash, バージョン 4.4.19だった >>674
いやbash前提じゃないんだ。POSIX シェル前提。POSIX シェルだけで動く。
だけどbashがインストールされていれば拡張機能が使える
その拡張機能っていうのも、本質的な機能じゃなくて
なんていうかな、同じコマンドで実行結果も同じだけど情報が詳細に取れるって感じ
BSD系といってもMacはデフォルトでbashになったのは知ってる。
パスに関してはシバンではなくbashコマンド経由で実行すれば良いと思ってる
(切替可能なようにするのでどっちみちシバンには頼らないと思うし) >>675
うん、たしかCentOS系はbashへのシンボリックリンクになってたはず
Debian系はdashなので拡張POISXは使えない POSIX規定外のことをPOSIX拡張って書くのは誤解のもとだからやめた方がいい。
伝統的にシェルにはなくてPOSIXで増えた機能のことをPOSIX拡張とよぶことがあるので、意味が真逆。 可搬性を考えるとPOSIX準拠で書く癖をつけた方が
自分のためになるなあ ★★★The● best way to eliminate too much gap between rich and poor, is to decide the tax● rate of the progressive tax in the referendum(Root Tax).★★★
この掲示板(万有サロン)に優●秀な書き込みをして、総額148万円の賞金をゲットしよう!(*^^)v
● http://jbbs.livedoor.jp/study/3729/ →リンクが不良なら、検索窓に入●れる! POSIX準拠で書くのは結構つらいので
デファクトスタンダードであるbash機能のみで書くといいよ >>676
いっその事
ps -p $$ -o comm=
でシェルを見て処理を分けるとか >>682
見分け方は本質的なところじゃないんで
俺にとってはどうでもいい話だけど、
シェルの判定ではなくて機能チェックで見分けるよ
ブラウザをUserAgentで判定するのではなく
使いたい機能が使えるかどうかで判定するのと似たようなやり方ね
> ps -p $$ -o comm=
ちなみにそれcygwinではエラーになった >>683
bash や zsh の機能を多く使うならば、その都度機能が動くかどうか判定するより、
起動シェルを見て分岐したほうが手間がないという意味合いで書きました。
cygwin の ps は -o オプションは無いのですね。失礼しました。
freebsd の環境で hoge.sh に ps -p $$ -o comm= と書いて、
/usr/local/bin/bash ./hoge.sh とすると、bash と表示されます。
(debian とかの ps でも ok)
cygwin の /bin/sh は /bin/bash なのでシェルを見るよりその都度使用する機能を判定するほうがよさそうですね。 > > ps -p $$ -o comm=
> ちなみにそれcygwinではエラーになった
マジで!? POSIXでも定義されてるんだけどなぁ 壊れた動画探しに
ffmpeg -i input -f null /dev/null 2>&1 | grep -m 1 -Ii error
ffmpeg -i input -f null /dev/null 2>&1 | grep -q -i error
とやったのですけれど、grepにヒットしてもffmpegの動画読み込みが止まりません。
grepにヒットしたらffmpegを止めるにはどうすればいいのか。
いいアイデアはないですか。 ffmpeg -xerror は使えないんだっけ
‘-xerror (global)’
Stop and exit on error bashがどうしても使えない化石サーバは廃品回収へ 新Mac板から来ました。
カレントディレクトリ内にaacファイルがあれば、mediainfoでHE-AACかLC-AACか調査して、
l-smash muxerを使ってHE-AACとLC-AACでは別のコマンドにてm4aに詰め込みたいです。
以下のままだと、aacが無い場合は
-----
aacファイルはありません.
-----
iTunes MP4 muxing mode
./test.sh: 3 行: 27777 Segmentation fault: 11 muxer -i $file 〜
と表示されてしまいます。
AACファイルがない場合はmuxerのコマンドに進まないようにするにはどう書くといいんでしょうか?
続く 続き
#!/usr/bin/env bash
for file in *.aac; do
if [ -e $file ] ; then
# aacファイルが存在する場合
mediainfo $file | grep '^Format profile *: LC$' >/dev/null 2>&1
#es=$?
else
echo "-----"
# aacファイルが存在しない場合
echo "aacファイルはありません."
fi
#------------------------------------
# HE-AACだった場合
if [ $? = 1 ] ; then
echo "-----"
muxer -i $file?sbr --file-format m4a -o ${file%.aac}.m4a
fi
#------------------------------------
#AACの場合
if [ $? = 0 ] ; then
echo "-----"
muxer -i $file --file-format m4a -o ${file%.aac}.m4a
fi
# .aacで終わるファイル名だけど、HEでもLEでもどちらでも無い場合はどうやるんだろうか
done $1は本当に直前に実行したのコマンドのステータスしか入らないから注意
ifで比較する前にecho $1で何が入ってるか確かめてごらん
それをふまえて論理構造組み直しな 「AACの場合」の前後の fi と if ... の2行を else に置き換えた上で、mediainfo コマンドの直後に
「HE-AAC だった場合」以降の if/else 文を移動、ではだめかな。
$? はすぐに別のコマンドの結果を格納してしまうので、
. mediainfo ...
. MEDIAINFO_RET=$?
. ...
. if [ $MEDIAINFO_RET = 0 ] ; then ...
みたいにコマンド実行直後に別の変数に回収してみるといいかもね。 >>687,>>689
ありがとう
早速試してみる >>692
>>693
どうもありがとうございます
試してみます
ググっていたら、同じようにエラーが出る人がいて、testの[]を二重のカッコ[[]]にしてるようでした
今見返したら、es=$?でやってみようと思ってたのに途中になってたっぽいです >>692
>>693
うまくいきました。
どうもありがとうございます。
>>696
書き換え前のスクリプトで試しましたが、こちらもエラーが出ませんでした。
どうもありがとうございます。
オライリーの入門bashには、ループの終了にbreakを使うのは良くないと書いてありました。
それはなぜでしょうか? >>697
今回みたいなエラー処理の場合はexitした方が良いかもね >>690
>>691
>>693
上の場合はmediainfoの終了ステイタスで判断して次にさらにif〜と進んでいるんですが、
LCかHE-AACかそのどっちでも無いかをcace〜muxer〜っとやるほうが確実なのかなと思いました。
その場合、
mediainfo $FILE | grep -E '^Format profile *: LC$|^Format profile *: HE-AAC / LC$'
で出てきた文字列をcaseに渡したいんですが、
case HOGE in
"*: LC" ) muxer〜;;
"*: HE-AAC / LC" ) muxer〜;;
* ) "AACではない";;
ecase
だった場合、HOGEにはどう書けばいいんでしょうか? >>700
HOGE=$(mediainfo $FILE | grep -E '^Format profile' | sed -E 's/.*: (.*)/\1/‘)
mediainfoの出力をgrepでFormat profileの行だけに絞って
それをsedで必要な箇所だけ置換して変数に入れてる
円マークはバックスラッシュに変換してね sedじゃなくbashのstring manipulationを使いたければ
HOGE=$(mediainfo “$FILE” | grep -E '^Format profile')
case ${HOGE##*:} in
…
${HOGE##*:} はHOGE内の文字列を先頭から”:”まで最長一致で取り除く 自己レス
HOGE=`mediainfo $FILE | grep -E '^Format profile *: LC$|^Format profile *: HE-AAC / LC$'`
case $HOGE in
これで大丈夫そうですね >>701
>>702
どうもありがとうございます
今触れないので、あとでやってみます! 文字列を1文字ずつ処理するってどうやれば良いのかな? 先頭の一文字を削除するっていうのはできるんだけどなぁ。
一文字削除したらな、その削除した一文字を取りたいものだ
あ、POSIXの話ね あ、これでいけるのか
str=abcdefg
last=${a##?}
echo ${str%%$last} >>706
速いかどうか分からんけど awk でやるなら
printf 'Hello\nWorld\n' | awk -F '' '{for(i=1;i<=NF;i++) print $i}'
とかかなぁ。grep -Eo '.' ってのもあるけど
for c in $(printf 'Hello\nWorld\n' | grep -Eo '.')
do
echo "$c"
done
あとは fold コマンドとかで。
printf 'Hello\nWorld\n' | fold -w1 間違えてた
str=abcdefg
last=${str##?}
echo ${str%%$last} >>712
後出しだけど、意外と改行まで1文字として扱うのは大変なんだよね 改行を扱いたい場合は bash か zsh の read かな。
printf 'Hello\nWorld\n' | while read -r -n 1 c;do echo "$c"; done
awk の場合は RS に '\0' をセットすればいいかも
printf 'Hello\nWorld\n' | awk -vRS='\0' -F '' '{for(i=1;i<=NF;i++) print $i}' 公開するようなシェルスクリプトって--helpオプションくらいは付けたほうがいいかな。
問答無用で第一引数をファイル名やらURLやらだと解釈するほうがはるかに楽だし簡潔になるんだけども。 オプションで思い出した。
オプションの解析めんどくせーとか思って他の言語のライブラリを参考に
オプション解析のライブラリを作ろうかと思ってるんだが、
getoptやgetopts程度だと使いやすくなった気がしないし、
作った所でそんなに簡単に書けるようなもんでもなさそうで、
何のためにコレが必要なんだ?って思いなした結果
他言語にあるようなライブラリは、--helpを半自動で
生成してくれるものだと思ってたりする
でももう少しオプションの解析楽にならないかな?
どうすればいいんだろう ある文字列のハッシュ値を求めたいんですけど
どのLinux/UNIX/FreeBSDでも標準ではいってる
ハッシュ化コマンドって何がありますかね?
それからPOSIX標準コマンド?みたいなものってあるんですか?
どこでも絶対はいっていなければいけないコマンドとか DATE=`date '+%Y%m%d%H%M'`
TMPDIR='/tmp'
BAKDIR='$TMPDIR/backup_%DATE'
#echo "$DATE"
mkdir $TMPDIR/backup_$DATE
cp -rfp /home/atashi/doc $BAKDIR
これだとcpが出来ないんですがどこが間違えていますか? 最後の行は
cp -rfp /home/atashi/doc $BAKDIR/docです >>720
3行目の%が間違ってるよね?
あと
4行目でデバッグ用にechoすべきは$BAKDIR
5行目はmkdir “$BAKDIR”
一つ一つ確認したほうがいいよ >>720
BAKDIR='$TMPDIR/backup_%DATE'
こうじゃない?
BAKDIR='$TMPDIR/backup_$DATE' >>723
>>724
ほんとだ。
$に直したんですが、
DATE=`date '+%Y%m%d%H%M'`
TMPDIR='/tmp'
BAKDIR='$TMPDIR/backup_$DATE'
echo "$TMPDIR"
#mkdir "$BAKDIR"
echo "$BAKDIR"
を実行すると、
$ ./hoge.sh
/tmp
$TMPDIR/backup_$DATE
になってしまって、ダメでした。
mkdir "$BAKDIR"
を入れると、カレントディレクトリに「$BAKDIR」というフォルダが出来てしまいました >>726
シェル シングルクォートとダブルクォートの違い 辺りでググってみて DATE=`date '+%Y%m%d%H%M'`
TMPDIR='/tmp'
BAKDIR="$TMPDIR/backup_$DATE"
echo "$TMPDIR"
mkdir "$BAKDIR"
echo "$BAKDIR"
>>727
出来ました!
どうもありがとうございました。
$ ./test.sh
/tmp
/tmp/backup_201803250316 ■ このスレッドは過去ログ倉庫に格納されています