トップページunix
1001コメント312KB

シェルスクリプト総合 その19

■ このスレッドは過去ログ倉庫に格納されています
0001シェルスクリプトライター2011/12/10(土) 20:06:40.38
シェルスクリプトの総合スレです。
スクリプトのお勉強・自慢・腕試しなどにどうぞ。

□お約束
・特記なき場合はBourne Shell(/bin/sh)がデフォルトです。
 bash/zsh/ksh/ashなどに依存する場合は明示しましょう。
 Linuxユーザは/bin/shの正体がbashなので特に注意。
 FreeBSDユーザは/bin/shの正体がashなので注意。
 v7 shに一番近くて、現役のshは、OpenSolaris由来のheirloom sh。
  http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/cmd/sh/
  http://heirloom.sourceforge.net/sh.html
・csh/tcshのシェルスクリプトは推奨されません。
 (理由は「csh-whynot」でググれ)
・UNIXにはシェルスクリプトに便利な小さなコマンドがいろいろあります。
 manや参考リンクを見ましょう。
 aproposないしはman -kでそれらしい単語による簡単な検索もできます。
・シェルスクリプトのことをシェルってゆーな
・シェルで使えるワイルドカード等は正規表現ではありません。
 正規表現の話題はスレ違い(正規表現スレへ)

□初心者へのアドバイス:
・適した道具を判断するのも頭の重要な使い方。シェルスクリプトよりも
 RubyやPerlの方が適した仕事には素直にそちらを使いましょう。
・知らないコマンドが出てきたらmanを引きましょう。
・思い通りに動かないときは、まずは sh -x でトレースしましょう。

前スレ
シェルスクリプト総合 その18
http://hibari.2ch.net/test/read.cgi/unix/1308195527/

次スレは >>970 で。
0069名無しさん@お腹いっぱい。2011/12/31(土) 15:02:13.88
>>66
こんな便利なものが!
getoptsを利用すれば、そもそも -で始まるものを除いた個数を数える必要もない
ことがわかりました。これで行きます。

まさに、forとかでループしたりせずに一発でできる方法ですね。
0070名無しさん@お腹いっぱい。2011/12/31(土) 15:39:18.97
>69
0071名無しさん@お腹いっぱい。2011/12/31(土) 15:44:42.43
チンコもマンコも使わなくていいって事ですね。
でもちょっと寂しいです。
0072名無しさん@お腹いっぱい。2011/12/31(土) 16:46:43.68
>>68
おおッ。気付いてませんでした
0073名無しさん@お腹いっぱい。2011/12/31(土) 16:54:05.71
getopts使ってもループは要ると思うが…
0074名無しさん@お腹いっぱい。2012/01/06(金) 01:39:38.05
あるプロセスを起動して、その標準出力から100行分の入力をもらったら、
そのプロセスを自動的に殺すようなスクリプトは書けますでしょうか?
または、あるプロセスを100秒だけ起動して、その後自動的に強制的に殺すこと
は可能でしょうか。
0075名無しさん@お腹いっぱい。2012/01/06(金) 02:49:26.47
>>74
>あるプロセスを起動して、その標準出力から100行分の入力をもらったら、
>そのプロセスを自動的に殺すようなスクリプトは書けますでしょうか?
あるプロセス | head -n 100
でいいんでねえの?

>または、あるプロセスを100秒だけ起動して、その後自動的に強制的に殺すこと
>は可能でしょうか。
あるプロセス &
sleep 100
kill -9 あるプロセスのプロセスID
でいいんでねえの?
0076名無しさん@お腹いっぱい。2012/01/06(金) 07:22:26.85
>>75
$! 使えよ
0077名無しさん@お腹いっぱい。2012/01/06(金) 13:58:48.37
>>75
上の方法はあるプロセスが101行目を出力しようとするまで生きてないか?
0078名無しさん@お腹いっぱい。2012/01/06(金) 15:00:15.02
困るの?
0079名無しさん@お腹いっぱい。2012/01/06(金) 15:01:39.72
もともとの仕様は満たしてないな。
困るかどうかは>>74に聞かないとわからん。
0080名無しさん@お腹いっぱい。2012/01/06(金) 19:01:28.82
$!って、拡張機能だったかと
0081名無しさん@お腹いっぱい。2012/01/06(金) 19:36:33.32
ボケるならもうちょっと面白いの頼む
0082名無しさん@お腹いっぱい。2012/01/06(金) 23:10:50.34
どうせ学校の宿題だろ

そんなもん、動きゃいいんだよ
0083名無しさん@お腹いっぱい。2012/01/06(金) 23:23:54.66
ボケてないよ。
0084名無しさん@お腹いっぱい。2012/01/07(土) 09:35:59.68
ボケがウケなかったからって開き直らなくていいよ
0085名無しさん@お腹いっぱい。2012/01/07(土) 10:56:05.85
>>75
ありがとうございます。
最初の問題については、たとえ延々と最初のプロセスが出力をたれ流していても、
パイプがなくなれば自動的に殺されるんですね。

後者の問題の場合、事前にプロセスIDが分からないのが問題なのですが、
シェルで、起動した命令のプロセスIDを取得する方法があるのでしょうか?
0086名無しさん@お腹いっぱい。2012/01/07(土) 11:44:27.31
>>85
だから $! だって >>76 が回答してるだろ
0087名無しさん@お腹いっぱい。2012/01/07(土) 15:13:56.59
>>76>>86 も、なぜこんな簡単なサンプルスクリプトさえ提示していただけないのでしょうか。

あるプロセス &
hoge=$!; sleep 100; kill -9 $hoge
0088名無しさん@お腹いっぱい。2012/01/07(土) 15:30:29.18
>>87
変数が無駄だな

sleep 100; kill -9 $!
だけで桶。
あと、>>76 の回答でわからない人間にはサンプルを示しても無駄。
0089名無しさん@お腹いっぱい。2012/01/07(土) 15:41:35.40
その $! に入ってるプロセスIDは、Sleep のものだとわかった上で言ってるのでしょうか…
0090名無しさん@お腹いっぱい。2012/01/07(土) 15:43:03.63
>>89
わかってないのはオマエ sleepのプロセスIDは $! に入らない。
0091名無しさん@お腹いっぱい。2012/01/07(土) 15:45:16.77
確かに、>>89 みたいなこと言ってる奴にサンプル示しても無駄だな。

正解者に間違った指摘する >>89 とかは書き込み自粛してくれ
0092名無しさん@お腹いっぱい。2012/01/07(土) 15:49:41.76
「バッググランドで実行された」 直前のプロセスID…だろw
>>88 で桶。
0093892012/01/07(土) 15:55:44.03
自粛はしない。
すっげぇ数の訂正レスがついて嬉しい。ありがとう。
0094名無しさん@お腹いっぱい。2012/01/07(土) 19:01:59.74
>>87-93
自作自演(・A・)イクナイ!!
0095名無しさん@お腹いっぱい。2012/01/07(土) 19:02:53.77
ばーれたか
0096名無しさん@お腹いっぱい。2012/01/07(土) 19:42:18.58
普段シェルbashとか使用しませんが
ファイルにある特定の文字でファイルを分割したいと考えています
何とか調べて
例えばkiritoriと言う文字の箇所からsample.txtを2つに分割するのに
grep -n kiritori sample.txtで行番号がわかりますので
split -ln sampl.txtによりxaa,xabの二つのファイルに分割することがわかりました

ここでお尋ねしたいのですが
grepとsplitをまとめて処理するためにはnをsplitに引き渡す方法がわかりません
どうかよろしくお願いいたします

又他の方法が有りましたらお教えください
0097名無しさん@お腹いっぱい。2012/01/07(土) 20:12:54.18
sed '1,/kiritori/{ w xaa
d
}' sample.txt > xbb
0098名無しさん@お腹いっぱい。2012/01/07(土) 21:06:49.01
>>97
早速のレス有難う御座いました。
おかげでファイルを分割できるようになりました

cutで行番号のみ抜き出したり
sedは文字変換のみの認識しかなかったりで
にわかもんはあなたのスクリプト見ただけで
すごいなと言う感想のみです

たまには勉強します
有難う御座いました
0099名無しさん@お腹いっぱい。2012/01/08(日) 17:43:52.18
bashのことで質問させてください。

dateでYYYYMMを取得してきます。(例:201201)
1月から9月の場合には0を消去して、YYYYMで表示させたいのです。(例:20121)
自分でも一応下のようにして出来たのですが、もっと簡単に作る方法はないでしょうか?

lm=`date "+%Y%m"`
ll=`echo ${lm}|cut -c 1-4`
mm=`echo ${lm}|cut -c 5-6`
if [ ${mm} -lt 10 ];then
mm=`echo ${mm}|sed s/0//`
fi
lm=${ll}${mm}
echo ${lm}
0100名無しさん@お腹いっぱい。2012/01/08(日) 17:47:39.30
>>99
date +%Y-%m | sed s/-0//
0101名無しさん@お腹いっぱい。2012/01/08(日) 17:49:53.71
>>100
それ、10月以降が駄目だろw

date +%Y%m | sed 's/-0//; s/-//'
0102名無しさん@お腹いっぱい。2012/01/08(日) 17:51:50.20
>>100-101
自演乙 おちつけ

date +%Y-%m | sed 's/-0//; s/-//'
0103名無しさん@お腹いっぱい。2012/01/08(日) 17:58:24.55
>>100-102
おぉ、もう返答が。

そうか、消したい0の前に何か付け足すことで限定すれば良いんですね。
ありがとうございました。
0104名無しさん@お腹いっぱい。2012/01/08(日) 18:03:08.42
>>99
せっかくbashならdateコマンド以外、内部コマンドだけでもできるよ。

a=($(date +'%Y %m'))
echo ${a[0]}${a[1]#0}
0105名無しさん@お腹いっぱい。2012/01/08(日) 18:57:49.32
month=`date +%m`
case $month in
0?) month=${month##0} ;;
esac

lm=`date +%Y$month`

or if you are using GNU date

date +%Y%-m
0106名無しさん@お腹いっぱい。2012/01/08(日) 19:15:43.51
bash環境ならGNU dateだろうし、
>>105 最終行の
date +%Y%-m
が最適解だな。

ところで、>>105 前半のcase文は意味ない。場合分けせずに ${month#0}だけでよい。
0107名無しさん@お腹いっぱい。2012/01/08(日) 19:52:34.83
>>99です。

man dateで>>105さんのやり方がしっかり書いてありました…
お恥ずかしい。

皆様ありがとうございます。
0108名無しさん@お腹いっぱい。2012/01/08(日) 22:51:02.00
いいね、これ。
今まで expr で 0 を消してた。
0109名無しさん@お腹いっぱい。2012/01/09(月) 08:29:51.48
exprで0消しならこうか
echo $(date +%Y)$(expr $(date +%m) + 0)
0110名無しさん@お腹いっぱい。2012/01/09(月) 08:53:59.96
あるいは
expr `date +%m` : "0\?\(.\+\)"
0111名無しさん@お腹いっぱい。2012/01/09(月) 09:00:41.79
先頭に0が付いてると数値演算でハマることがあるよね
$ echo $((08+0))
-bash: 08: value too great for base (error token is "08")
0112名無しさん@お腹いっぱい。2012/01/09(月) 09:08:48.76
>>111
シェルの算術式とかでは頭の0は8進数扱いだからな。
exprなら8進数とはみなさないので、expr 08 + 0 とかやって0を消すのは常識テクニック。
0113名無しさん@お腹いっぱい。2012/01/13(金) 10:37:37.37
質問させてください。

sed -e "s/ //g" temp.txt
↑ここに/(スラッシュ)を含む文字列を入れたいときはどうしたらいんですか?
0114名無しさん@お腹いっぱい。2012/01/13(金) 10:46:55.51
sed -e "s/ /\//g" temp.txt
0115名無しさん@お腹いっぱい。2012/01/13(金) 11:12:47.98
sed -e "s@ @/@g" temp.txt
>>114のようにするか、区切り文字を"/"から"@"とかの他の文字に変える

純粋なsedの話しなのでスレち
ttp://toro.2ch.net/test/read.cgi/unix/1085730992/
0116名無しさん@お腹いっぱい。2012/01/19(木) 17:43:20.44
solarisのfindで、yyyymmddHHMMSS形式のディレクトリ一覧から、任意の日付以降のディレクトリを対象としてtarファイル化処理を実行したいと考えています
中間ファイルを作ってディレクトリ名と日付の比較を行なっての処理は一応出来たのですが、出来ればfind xargsで一発で出来る形にしたいです
中間ファイル無しで上記のような処理は実現できるでしょうか?
0117名無しさん@お腹いっぱい。2012/01/19(木) 17:54:34.58
>>116
yyyymmddHHMMSS形式なら単純な数値と考えて大小比較すればいいね。
比較するのに中間ファイルは要らない。
0118名無しさん@お腹いっぱい。2012/01/19(木) 18:05:56.97
>>117
エスパーカが足りないなw
中間ファイルは条件にマッチしたディレクトリリストのファイルかと

tarじゃなくてcpioなら Solaris findの -cpioオプションで一発かな
0119名無しさん@お腹いっぱい。2012/01/19(木) 18:35:22.45
該当するディレクトリのタイムスタンプがディレクトリ名と同じになっていれば -newer のたぐいが使えるのでは?
0120名無しさん@お腹いっぱい。2012/01/19(木) 18:52:00.33
>>119
-newer のたぐいはまさに中間ファイルが必要ですが
0121名無しさん@お腹いっぱい。2012/01/19(木) 18:56:59.65
一番古い対象ディレクトリを指定すればOKじゃないかな。
0122名無しさん@お腹いっぱい。2012/01/19(木) 19:01:28.69
最初の質問の中間ファイルを使う処理をスクリプトのまま書き込んだ方が話が早いと思うが…
どんな内容の中間ファイルをどのような目的で作成しているのか
それがわかれば回答する側もエスパーにならなくていいからね
0123名無しさん@お腹いっぱい。2012/01/19(木) 19:02:52.93
皆さんの書き込みをヒントに自己解決しました。ありがとう。
0124名無しさん@お腹いっぱい。2012/01/19(木) 19:04:30.79
>>123
どうやって?
0125名無しさん@お腹いっぱい。2012/01/19(木) 23:32:23.93
このファイルからランダムで1行だけ選択して
/home/my/st/1.txt
1 暑い
2 寒い
3 少し暑い
4 少し寒い
5 すごく暑い
6 すごく寒い

/home/my/st/2.txtに書き込ませたいですのですがどう書けば良いでしょうか?
0126名無しさん@お腹いっぱい。2012/01/19(木) 23:50:32.46
cat 1.txt | shuf | head -1 > 2.txt
0127名無しさん@お腹いっぱい。2012/01/19(木) 23:57:33.44
>>126
わお
そんなに短く出来るんですね
感謝です!!
0128名無しさん@お腹いっぱい。2012/01/20(金) 00:02:25.13
shuf なんてコマンドあるんだ… ひとつ知識がふえた。ありがとう。
調べたらこんな感じにもできそうだった。
shuf --rndom-source=1.txt -o 2.txt
0129名無しさん@お腹いっぱい。2012/01/20(金) 00:34:25.58
こう使う。
--random-source=/dev/random
--random-source=/dev/zeroで試してみて
0130名無しさん@お腹いっぱい。2012/01/20(金) 01:03:57.15
Perlの本に、1行ずつ入力を読んで、ランダムな1行を残す方法が載ってたな。
全行シャッフルとかしなくていいのがメリットだとか。

perl -ne '$result = $_ if rand($.)<1; END{print $result}'
0131名無しさん@お腹いっぱい。2012/01/20(金) 01:24:54.34
負担が軽いのが一番良いしそれが一番難しい
0132名無しさん@お腹いっぱい。2012/01/20(金) 06:30:44.02
>>126
catもパイプも無駄だな。

shuf -n 1 < 1.txt > 2.txt
0133名無しさん@お腹いっぱい。2012/01/20(金) 08:44:30.17
最初のリダイレクトも不要であろう…
0134名無しさん@お腹いっぱい。2012/01/20(金) 11:04:53.93
shuf って GNU だけだよね?
0135名無しさん@お腹いっぱい。2012/01/20(金) 13:37:16.04
グニュー特選隊
0136名無しさん@お腹いっぱい。2012/01/20(金) 13:55:53.73
>>134
POSIXにはない
0137名無しさん@お腹いっぱい。2012/01/20(金) 19:02:11.53
比較的最近のcoreutilsから

へどらとかならあるんじゃね?
0138名無しさん@お腹いっぱい。2012/01/20(金) 19:09:47.87
いやいや、5年くらい前からあるよ。
0139名無しさん@お腹いっぱい。2012/01/20(金) 19:11:08.12
5年が最近なんだろう。
0140名無しさん@お腹いっぱい。2012/01/20(金) 19:58:46.80
バグ出し終わるまで3年は寝かせるよな
0141名無しさん@お腹いっぱい。2012/01/20(金) 22:33:57.03
ファイルからランダムに1行取り出すのにshufを使うのは
大手SIerじゃ5年前から常識だな
0142名無しさん@お腹いっぱい。2012/01/20(金) 23:18:45.09
shufはcoreutils 6.0(2006-08-15)からみたいだね。
0143名無しさん@お腹いっぱい。2012/01/20(金) 23:39:27.25
改行区切りのテキストを、コマンドの引数で渡すために、コンマ区切りにしようとこう書いた
A="`cat list.txt |tr -s \\n , |head -c -1`"
でもこれだとnが,に置換される。\\nを'\n'にすればいけたけど、バッククオートの中のエスケープって何回評価されるの?
0144名無しさん@お腹いっぱい。2012/01/21(土) 00:28:29.06
つ echo "\\n"
\n
0145名無しさん@お腹いっぱい。2012/01/21(土) 01:02:25.99
ログインするシェルってどうかいたらいいんでしょうか?
ユーザーアカウント名とパスワードを自動で入れてログインしたいのですが
0146名無しさん@お腹いっぱい。2012/01/21(土) 02:12:35.16
>>143
http://linuxjm.sourceforge.jp/html/GNU_bash/man1/bash.1.html#lbBA
A="$(cat list.txt |tr -s \\n , |head -c -1)"
0147名無しさん@お腹いっぱい。2012/01/21(土) 07:43:37.27
>>143
とりあえずcatが無駄。

バッククオートの中のバックスラッシュは特殊な意味を持つので、
2回評価されると考えとけばいい。
この例ではバックスラッシュ3回で回避できる。

>>146
$( )使っちゃ駄目よ。
0148名無しさん@お腹いっぱい。2012/01/21(土) 08:02:20.86
俺も cat でファイル読む人なんだが、
なぜ cat をわざわざ付けるかと言うと、

自分が作る全てのスクリプトや打ち込むコマンドで
ファイルを読む際には必ず cat で読む

と統一する事で、ミスる可能性を減らしてるつもり。
客に納品するスクリプトでは仕方なく削るけど。

元々のきっかけは 20 年程前に tar でファイル消してしまったことだった。
0149名無しさん@お腹いっぱい。2012/01/21(土) 08:09:55.16
無駄なcatを入れないのは大手SIerじゃ常識だね。
0150名無しさん@お腹いっぱい。2012/01/21(土) 08:51:51.18
コンピュータ的には無駄だが人間的には無駄ではない。
コメントの様な物だ。
0151名無しさん@お腹いっぱい。2012/01/21(土) 08:55:23.20
cat file | コマンド

の順に書きたいというだけの理由なら、

< file コマンド

と書けば良い。
01521432012/01/21(土) 09:25:54.14
>>146
凄く参考になった。ありがとう

> バッククォートを使う古い形式の置換を用いたとき、バックスラッシュは文字通りの意味を保ちます
> が、 $, `, \ の前にある場合は例外となります。バックスラッシュが前置されていないバッククォートが
> あると、そこでコマンド置換は閉じられます。 $(command) という形式を用いたときは、括弧の間にある
> 全ての文字がコマンドとなります。特別扱いされる文字はありません。
`cmd`と$(cmd)の2つの形式で意味(処理?)が違うなんて知らなかった

> コマンド置換は入れ子にできます。バッククォート形式の時に入れ子を行うには、
> 内側のバッククォートをバックスラッシュでエスケープします。
入れ子にするときは2つの形式を組み合わせて書いてたから、これも気が付かなかった
特殊扱いの文字が$(cmd)形式ではないなら、cmdの中に)がある場合\でエスケープはできないってことか

>>147
評価回数について、ありがとう。最小3つ並べる必要があるのか

cat使っているのは、前処理があってパイプを使っているという意思表示
ときどきシーク可能か否かで挙動が変わるプログラムもありますし
0153名無しさん@お腹いっぱい。2012/01/21(土) 10:01:25.74
>>147
> $( )使っちゃ駄目よ。

爺さんや、Solarisでも/bin/shはkshになりましたよ。
というわけでもういいんじゃないか。テンプレもそろそろ修正どき。
0154名無しさん@お腹いっぱい。2012/01/21(土) 10:17:31.18
>>151
正直、その書き方は知らなかった。
0155名無しさん@お腹いっぱい。2012/01/21(土) 10:26:19.10
>>148
同じ様な経験から自分と似たような対処している全然知らない人の存在を知ると、何か和むなぁ
0156名無しさん@お腹いっぱい。2012/01/21(土) 10:28:50.43
$( )を使わないのは大手SIerじゃ常識だね。
0157名無しさん@お腹いっぱい。2012/01/21(土) 11:57:08.26
>>148
なんかずれてる気がする
0158名無しさん@お腹いっぱい。2012/01/21(土) 12:16:06.97
>>148
tar xvf と tar cvf を間違えたって話か?

ふつーあらかじめ chmod -w hoge.tar やっておくから消すことはありえない。
0159名無しさん@お腹いっぱい。2012/01/21(土) 12:33:47.90
sudo tar cvf 〜 なんてしちゃったり
0160名無しさん@お腹いっぱい。2012/01/21(土) 13:25:25.15
ftpmailで取得してた20年前じゃあるまいし、tarなんか消しちゃっても全然問題ないだろ。
0161名無しさん@お腹いっぱい。2012/01/21(土) 13:29:30.84
tar cvf aaa.tar bbb ccc
とやろうとして
tar cvf bbb ccc
とかやって、bbbを消しちゃったってことかな
0162名無しさん@お腹いっぱい。2012/01/21(土) 13:32:21.94
あぁ、もともとのきっかけは20年前か… ftpmailあり得るな。
0163名無しさん@お腹いっぱい。2012/01/21(土) 14:11:25.85
>>161
>>146 では「ファイルを読む際には必ずcatで読む」て言ってるから、
tar xvf の時の話かと。
01641482012/01/21(土) 23:40:04.95
>>158
> tar xvf と tar cvf を間違えたって話か?
それそれ。
0165名無しさん@お腹いっぱい。2012/01/22(日) 00:51:01.40
俺も先頭にcat付ける派。

cat foo | cmd1
で、cmd1の前に前処理やりたくなった時、
cat foo | cmd0 | cmd1
と書き換え簡単。
cmd1 fooをcmd0 foo | cmd1にするのは語順置き換えがある。
transpose-wordするのは空白入り"文字列やオプション指定があるとちょっと面倒。

ファイル名をひとつしか指定できないバカコマンドで、
2つ指定したくなった時にイラっとくるから、などの理由。
0166名無しさん@お腹いっぱい。2012/01/22(日) 01:25:14.86
俺はcatつけないな。後段のコマンドにもよるけど、
ファイルがないなどのエラーを捕捉し損ねる可能性があるから。
0167名無しさん@お腹いっぱい。2012/01/22(日) 06:46:04.29
むかし、cat foo | sed をここで書いて叩かれた思い出。
0168名無しさん@お腹いっぱい。2012/01/22(日) 08:04:14.62
>>165>>151 読んでないだろw

cat foo | cmd1 ではなく、
< foo cmd1 とする。(いつもこう書く癖を付けておく)

で、cmd1の前に前処理やりたくなった時、
< foo cmd1 | cmd2
と書き換え簡単。

以上で、catを無駄に使う理由がなくなる。
■ このスレッドは過去ログ倉庫に格納されています