シェルスクリプト総合 その13
■ このスレッドは過去ログ倉庫に格納されています
0001名無しさん@お腹いっぱい。
2008/10/16(木) 00:48:38スクリプトのお勉強・自慢・腕試しなどにどうぞ。
まずは注意点、リンク、地鎮祭など(>>1-6くらい)をご覧ください。
□お約束
・特記なき場合はBourne Shell(/bin/sh)がデフォルトです。
bash/zsh/ksh/ashなどに依存する場合は明示しましょう。
Linuxユーザは/bin/shの正体がbashなので特に注意。
・csh/tcshのシェルスクリプトは推奨されません。
(理由は「csh-whynot」でググれ)
・UNIXにはシェルスクリプトに便利な小さなコマンドがいろいろあります。
manや参考リンクを見ましょう。
aproposないしはman -kでそれらしい単語による簡単な検索もできます。
・シェルスクリプトのことをシェルってゆーな
・シェルで使えるワイルドカード等は正規表現ではありません。
正規表現の話題はスレ違い(正規表現スレへ)
□初心者へのアドバイス:
・適した道具を判断するのも頭の重要な使い方。シェルスクリプトよりも
RubyやPerlの方が適した仕事には素直にそちらを使いましょう。
・知らないコマンドが出てきたらmanを引きましょう。
・思い通りに動かないときは、まずは sh -x でトレースしましょう。
前スレ
シェルスクリプト総合 その12
http://pc11.2ch.net/test/read.cgi/unix/1218277263/
0553名無しさん@お腹いっぱい。
2008/12/27(土) 10:20:010554名無しさん@お腹いっぱい。
2008/12/27(土) 12:05:09case `ls -A hogedir` in '') 空の場合の処理;; esac
の方が、wcがいらない分まし。
0555名無しさん@お腹いっぱい。
2008/12/27(土) 14:40:01違います。すいません説明がたりませんでした。
ある領域に一致するものを、別の領域に置き換えるという意味です。
>>552の場合は、一致しないので置き換えないようになります。
0556名無しさん@お腹いっぱい。
2008/12/27(土) 14:41:290557名無しさん@お腹いっぱい。
2008/12/27(土) 16:36:16b.datの内容(3行全部)に完全に一致する部分を、
c.datの内容で置き換えるってことか。
0558名無しさん@お腹いっぱい。
2008/12/27(土) 16:45:17妥当だろうか。
ディレクトリのハードリンクについては考えないことにする。
0559名無しさん@お腹いっぱい。
2008/12/27(土) 16:45:550560名無しさん@お腹いっぱい。
2008/12/27(土) 16:50:31→ ディレクトリを含まないディレクトリ (通常ファイルはたくさんあっていい)
0561名無しさん@お腹いっぱい。
2008/12/27(土) 17:37:58だから、全然妥当じゃないねw
0562名無しさん@お腹いっぱい。
2008/12/27(土) 20:47:01MacOS XのHFS+→ファイルを作ると . へのンク数+1
Linuxのext3→ファイルを作ってもリンク数は増えない
となった。
0563名無しさん@お腹いっぱい。
2008/12/27(土) 20:57:40そのとおりです!
0564名無しさん@お腹いっぱい。
2008/12/27(土) 22:35:22おおむね妥当というか、定番の方法。
ただし、ダメなことがあるので、そのショートカットは省略できるように
色々なツールがオプション持ってるけどね。
0565名無しさん@お腹いっぱい。
2008/12/27(土) 22:45:04なんでやねん。
それは、ディレクトリが「leaf」かどうかを判断する方法であって、
「空」かどうかの判断にはなっていない。
ディレクトリ中に普通のファイルが存在しても、リンクカウントは2のままなんだから。
0566名無しさん@お腹いっぱい。
2008/12/28(日) 09:20:08それだとrmdirしてみるくらいしか思いつかないなぁ。
0567名無しさん@お腹いっぱい。
2008/12/28(日) 12:50:040568名無しさん@お腹いっぱい。
2008/12/28(日) 13:14:220569名無しさん@お腹いっぱい。
2008/12/28(日) 14:45:240570名無しさん@お腹いっぱい。
2008/12/28(日) 17:03:01awk駆使するしかないかなー。
何気にあってもいいような気がするんだけど、そんなコマンドないんだよねー。
0571名無しさん@お腹いっぱい。
2009/01/05(月) 01:38:25/home/hoge
ディレクトリにある全てのファイルの改行コードを全部LF変更して上書きするシェルスクリプトを書きたいのですが、どう書けばよいでしょうか??
nkf -w -Lu samble.txt > sample.text
のようにやればよいのは知っているのですが、全ファイル一気に処理したい場合どうすればよいか分かりません。
ご教示いただけると幸いです。
0572名無しさん@お腹いっぱい。
2009/01/05(月) 01:39:17↓
LFに変更
です。。
0573名無しさん@お腹いっぱい。
2009/01/05(月) 02:19:31> nkf -w -Lu samble.txt > sample.text
> のようにやればよいのは知っているのですが
あかんで!
perl -iでも使えば。Encode/jcodeで変換して。
シェルスクリプトなら、
for i in *.txt; do
nkf -w -Lu $i > $i.--$$--
# 必要ならば yes no | mv -i $i $i.org
mv $i.--$$-- $i
done
0574名無しさん@お腹いっぱい。
2009/01/05(月) 07:44:10以下で桶。
↓
#!/bin/sh
cd /home/hoge
for file in *.txt
do
out_file=`basename "$file" .txt`.text
nkf -w -Lu "$file" > "$out_file"
done
>>573
> あかんで!
↑
なんであかんの?w
.txtと.textを読み落したか?w
上書きはしないよw
0575名無しさん@お腹いっぱい。
2009/01/05(月) 08:08:460576名無しさん@お腹いっぱい。
2009/01/05(月) 08:54:26% nkf -w -Lu --in-place=.bak *.txt
じゃね
0577名無しさん@お腹いっぱい。
2009/01/05(月) 09:53:01それはへ理屈。
>>573 の回答を見れば、.txtを.txtに変換しようとしてるので、
質問を読み間違えてるのは明らか。
しかも、スペース入りファイル名があると正常動作しないし。
知ったか回答者の典型。
0578名無しさん@お腹いっぱい。
2009/01/05(月) 12:43:33tr -d "\r" < input.txt > output.txt
で改行コードが変換できる。
0579名無しさん@お腹いっぱい。
2009/01/05(月) 18:58:46という命題は真ですか?
0580名無しさん@お腹いっぱい。
2009/01/05(月) 19:44:540581名無しさん@お腹いっぱい。
2009/01/05(月) 19:50:14ハイ、次の回答者。
0582名無しさん@お腹いっぱい。
2009/01/05(月) 20:03:210583名無しさん@お腹いっぱい。
2009/01/05(月) 20:39:020584名無しさん@お腹いっぱい。
2009/01/05(月) 21:00:140585名無しさん@お腹いっぱい。
2009/01/05(月) 21:15:470586名無しさん@お腹いっぱい。
2009/01/06(火) 12:55:50似たようなもんだな。
0587名無しさん@お腹いっぱい。
2009/01/06(火) 13:48:060588名無しさん@お腹いっぱい。
2009/01/06(火) 14:13:16perlがあってもsedやawkは使うしもちろんシェルスクリプトも使う
オナニー万歳
0589名無しさん@お腹いっぱい。
2009/01/06(火) 14:29:480590名無しさん@お腹いっぱい。
2009/01/06(火) 14:31:13perlぐらいフツーっすよ
0591名無しさん@お腹いっぱい。
2009/01/06(火) 14:32:560592名無しさん@お腹いっぱい。
2009/01/06(火) 14:39:18awkがメイン
0593名無しさん@お腹いっぱい。
2009/01/06(火) 14:43:020594名無しさん@お腹いっぱい。
2009/01/07(水) 00:45:440595名無しさん@お腹いっぱい。
2009/01/07(水) 00:54:34どうもうまくいかない。
>telnet POP3.net 110 > mail
user MYID
pass MYPASS
retr 1
quit
Connection closed by foreign host.
>
となりうまくいくんだけど、
telnet POP3.net 110 << EOF
user MYID
pass MYPASS
retr 1
quit
EOF
というのをリダイレクトすると
Connected to POP3.net
Escape character is '^]'.
Connection closed by foreign host.
となり全然駄目。
ものすごい致命的な間違い、勘違いがありそう。
とりあえずこれから
man telnet
www.ietf.org/rfc/rfc1939.txt
を一通り読むんですが、その前に間違いを指摘して下さいな。
0596名無しさん@お腹いっぱい。
2009/01/07(水) 01:07:340597名無しさん@お腹いっぱい。
2009/01/07(水) 01:13:230598名無しさん@お腹いっぱい。
2009/01/07(水) 01:27:44netcat,nc初めて聞きます。調べておこう。
>>597
どうも助かります。
>telnetは標準入力じゃなくてttyを入出力とするからリダイレクト出来ない
となると、
>telnet hoge.net < `tty` という具合ですね。
別の端末から
>echo "user MYID" > /dev/あっち とか。
telnetをやるスクリプトを動かすスクリプトを書いて
上位からttyへ流し込むとか。
いろいろやってみますね。
どうもサンクスです。
0600名無しさん@お腹いっぱい。
2009/01/07(水) 08:36:31UNIX FAQにあるように echo と sleep で
間隔開けて1行ずつ出せばいけたかも
0601名無しさん@お腹いっぱい。
2009/01/07(水) 09:58:530602名無しさん@お腹いっぱい。
2009/01/07(水) 10:01:37>>599
0603名無しさん@お腹いっぱい。
2009/01/07(水) 11:04:240604595
2009/01/08(木) 22:29:27セキュリティ絡みでひっかかって駄目みたい。
ok
ok
ok
とくるからpassまではいけてるから、そっから先に
いろいろあるんだろうなあ。
ぐぐったらperlのスクリプトが沢山あったから、
参考にしてみます。
0605571
2009/01/09(金) 02:21:36御礼遅れてすいません。ありがとうございました!
--overwrite なんてオプションあったんですね。知りませんでした。
あとすいません、573の方が書いてくださったシェルスクリプトの--&&-- ってのはどういう意味でなのでしょう?
ご教示いただきたくm(_ _)m
0606名無しさん@お腹いっぱい。
2009/01/09(金) 07:14:00こりゃ便利です。移行もスムーズです!!!
0607名無しさん@お腹いっぱい。
2009/01/09(金) 07:16:59--&&-- じゃなく --$$-- な。
echo --$$--
ってやってみ。
別に .aho でも .baka でも .tmp でもいいんだろうが、
.bak だと同名のファイルが既に存在していて壊してしまうおそれが高いし、
.--$$-- ならその可能性は低いだろうと、それだけのこと。
0608名無しさん@お腹いっぱい。
2009/01/09(金) 10:07:01シェルってゆうな。クズ。
0609571
2009/01/09(金) 11:29:11すいません、あとひとつお聞きしたいのですが
outfile=`basename "$file" .txt` .text
で、
""は必要ですか??
試してみると
outfile=`basename $file .txt` .text
でもできました。
0610名無しさん@お腹いっぱい。
2009/01/09(金) 11:39:040611名無しさん@お腹いっぱい。
2009/01/09(金) 11:39:54" " は必要。
" "がないと、スペースとか、特殊記号を含んだファイル名で正常動作しない。
0612名無しさん@お腹いっぱい。
2009/01/09(金) 11:45:58思った方がいいので当面必要無くても常にダブルクォートを付けるルールを守ること
その意味で >>573 は失格
0613名無しさん@お腹いっぱい。
2009/01/09(金) 11:47:57> 特殊記号を含んだファイル名
問題になるな特殊記号って?
0614名無しさん@お腹いっぱい。
2009/01/09(金) 11:48:530615名無しさん@お腹いっぱい。
2009/01/09(金) 11:51:310616名無しさん@お腹いっぱい。
2009/01/09(金) 11:58:140617名無しさん@お腹いっぱい。
2009/01/09(金) 12:14:32てくるなら)普通にメタキャラクタのチェックしないと危険だろ。
0618sage
2009/01/10(土) 14:55:140619名無しさん@お腹いっぱい。
2009/01/10(土) 15:11:30そんなコマンドは見たことないな。
置き換え対象のファイルがあまり大きくないならperlかなにかで
一つの文字列にまるごと読み込んで置換するのが楽だと思う。
メモリにおけないぐらいでかいファイルなら工夫が必要だけど
0620名無しさん@お腹いっぱい。
2009/01/10(土) 21:49:06今一何をしたいのかよく分からないんだけど、
a.datとb.datを評価して違っていればc.datに置き換えると解釈して書いてみた。
#!/bin/sh
File_line_digit(){
FLINE=`cat "$1" | wc -l | sed 's/[[:blank:]]*//'`
echo "$FLINE"
}
Use_line_strings(){
STRINGS=`head -n "$1" "$2" | tail -n 1`
echo "$STRINGS"
}
続く
0621名無しさん@お腹いっぱい。
2009/01/10(土) 21:49:26TWONUM=1
THREENUM=1
ONEEND=`File_line_digit "$1"`
TWEEND=`File_line_digit "$2"`
THREEEND=`File_line_digit "$3"`
while [ "$ONENUM" -ne `expr "$ONEEND" + 1` ]
do
ONESTR=`Use_line_strings "$ONENUM" "$1"`
TWOSTR=`Use_line_strings "$TWONUM" "$2"`
THREESTR=`Use_line_strings "$THREENUM" "$3"`
if [ "$ONESTR" = "$TWOSTR" ]
then
echo "$THREESTR"
ONENUM=`expr "$ONENUM" + 1`
TWONUM=`expr "$TWONUM" + 1`
THREENUM=`expr "$THREENUM" + 1`
else
echo "$ONESTR"
ONENUM=`expr "$ONENUM" + 1`
fi
done
0622名無しさん@お腹いっぱい。
2009/01/11(日) 01:00:39それ以前にブログラミングのセンスなさすぎだろ
関数名も変数名も処理内容もださすぎ
0623名無しさん@お腹いっぱい。
2009/01/11(日) 01:39:260624名無しさん@お腹いっぱい。
2009/01/11(日) 03:29:310625名無しさん@お腹いっぱい。
2009/01/11(日) 09:06:20とりあえず、頭の関数ひとつだけ添削してやるよw
File_line_digit(){
FLINE=`wc -l < "$1"`
echo $FLINE # ここは、わざとダブルクォートを付けない
}
↑のようにすれば、wc -l の出力のスペースをカットするために
わざわざsedを使う必要がない。あと、もちろんcatは不要。
wc -l の出力にファイル名が付かないように、
標準入力からリダイレクトする。
0626名無しさん@お腹いっぱい。
2009/01/11(日) 11:42:35自分で見直しても読みにくいの書いてすいません(藁
出直してきます。
>>625
目から鱗です。
0627571
2009/01/12(月) 13:28:520628名無しさん@お腹いっぱい。
2009/01/12(月) 14:18:39何故か思うように動かない。
考え方としては有っていると思うのだが、
最後のif文の比較がどうしても真にならない。
set -x して、トレースしたら同じ文字列のはずだし、
空白が混入しているようにも思えないのだが・・
0629628
2009/01/12(月) 14:22:44searchLine=`head -n 1 "$2"`
fullLines=`echo \`wc -l < "$1"\` `
matchLines=`echo \`wc -l < "$2"\` `
matchPat=`tr '\012' ' ' < "$2"`
while [ $fullLines -ge $matchLines ]
do
cmpPat=`tail -n $fullLines "$1" | \
awk '
$0 !~ /^'"$searchLine"'$/ {
print > "/dev/tty"
}
/'"$searchLine"'/{
cmpPat = $0
for ( i = 1 ; i < '"$matchLines"' ; ++i ) {
getline addLine
cmpPat = cmpPat " " addLine
}
print cmpPat
nextfile
}'`
if [ "$cmpPat" = "$matchPat" ]
then
cat "$3"
$fullLines=`expr $fullLines - $matchLines`
else
fullLines=`expr $fullLines - 1`
fi
done
tail -n $fullLines "$1"
0630628
2009/01/12(月) 14:37:35そもそも、そこに処理が行かないわけで・・
0631628
2009/01/12(月) 14:57:09trが最後に改行が無くても空白を追加していました。
しかし、どうも上記はまだまだ使い物になりません。
もう少し精進します。
てか、これ、結構ムズい。
0632628
2009/01/12(月) 15:29:55#!/bin/sh -
searchLine=`head -n 1 "$2"`
fullLines=`echo \`wc -l < "$1"\` `
matchLines=`echo \`wc -l < "$2"\` `
matchPat=`tr '\012' ' ' < "$2"`
while [ $fullLines -ge $matchLines ] ; do
cmpPat=`tail -n $fullLines "$1" | \
awk '
$0 !~ /^'"$searchLine"'$/ {
print > "/dev/tty"
nextfile
}
$0 ~ /^'"$searchLine"'$/{
cmpPat = $0
for ( i = 1 ; i < '"$matchLines"' ; ++i ) {
getline addLine
cmpPat = cmpPat " " addLine
}
cmpPat = cmpPat " "
print cmpPat
nextfile
}'`
if [ "$cmpPat" = "$matchPat" ]
then
cat "$3"
fullLines=`expr $fullLines - $matchLines`
fi
fullLines=`expr $fullLines - 1`
done
tail -n $fullLines "$1"
0633名無しさん@お腹いっぱい。
2009/01/12(月) 18:48:110634名無しさん@お腹いっぱい。
2009/01/12(月) 22:40:43そうは言うけど、>>551の設問を
>>557の要件で満たすのは大変だぞ。
現に誰もまともな解答例が無いだろう。
仮に三つのファイルを
1・対象ファイル
2・参照ファイル
3・置換ファイル
として、1の行数が不定の上、
2が何回マッチするかも不定で、
シェルスクリプト流に一行づつ処理するならば
途中でマッチしないことが判明したら、仕切り直さないといけない。
(でも、マッチしたらその分行を進めないといけない)
いっそC言語とかで書いて、2のファイルを読み出して保持しておき、
同じサイズのバッファをmallocとかで確保し、順次改行迄ごとに1のファイルを読んで、
strcmpとかする方が楽に思える。
無駄な処理が実に多いのは承知の上で、
tail と awk の合わせ技で料理したのに、
実に君は無礼だな。
0635名無しさん@お腹いっぱい。
2009/01/13(火) 02:34:49富豪的にrubyとかperlでやった方がマシでしょ
#!/usr/bin/env ruby
# -*- coding: utf-8; -*-
abort unless ARGV.size == 3
open(ARGV.shift).read.\
gsub(/#{Regexp.escape(open(ARGV.shift).read)}/m,open(ARGV.shift).read).\
display
まあワンライナーだよな
0636名無しさん@お腹いっぱい。
2009/01/13(火) 07:55:53cat a.dat b.dat c.dat | uniq
おまいら大丈夫か?
0637名無しさん@お腹いっぱい。
2009/01/13(火) 07:57:11>まあワンライナーだよな
ぷぷぷ
0638名無しさん@お腹いっぱい。
2009/01/13(火) 08:00:14>>557 を欲詠め
あなたの回答は全くの見当違い
0639名無しさん@お腹いっぱい。
2009/01/13(火) 12:20:07多大なるヒントを有難う。
やっぱりawkだけで書けるわ。
#!/bin/sh -
searchLine=`head -n 1 "$2"`
matchLines=`echo \`wc -l < "$2"\` `
cmpList=`tr '\012' ' ' < "$2"`
awk '
$0 !~ /^'"$searchLine"'$/ {
print $0
next
}
$0 ~ /^'"$searchLine"'$/ {
matchList = $0
for ( i = 1; i < '"$matchLines"'; ++i ) {
getline
matchList = matchList " " $0
}
matchList = matchList " "
if ( matchList ~ /^'"$cmpList"'$/ ) system("cat '"$3"'")
else {
sizeOfArray = split(matchList, pArray, " ")
for ( i = 1; i <= sizeOfArray; ++i)
print pArray[i]
}
}' "$1"
0640名無しさん@お腹いっぱい。
2009/01/13(火) 12:26:52BEGINで$1, $2のファイル読み込むといいと思うよ。
awkは(初期のを除いて)リダイレクト使えるから。
# すれ違いっぽいからコードは書かないけど
0641名無しさん@お腹いっぱい。
2009/01/13(火) 15:30:14有難う御座います。
awkでの正規表現中での変数展開のやり方が分からず、
先にシェルに展開してもらう方法をとっていましたが、
今調べたり、それを元にゴソゴソやって。
$0 ~ "^"var"$" { とか
if ( var1 ~ "^"var2"$" ) で、
どうやら完全一致を取り出せそうなことが分かりました。
0642名無しさん@お腹いっぱい。
2009/01/13(火) 16:42:25右辺が文字列型の値の場合は正規表現パターンを表すとみなす。
だからawk -v arg1="XXX"{ BEGIN { searchLine=arg1 } $1 ~ searchLine { ...って出来る。
awk本のオリジナルawkは、"-v"なしで代入式だけを書かないといけないんだけど。
"-v"はnawk移行かな? 今はほとんど全部"-v"が必要なはずと思う。
0643名無しさん@お腹いっぱい。
2009/01/13(火) 16:43:35と書いたつもりでした。
0644名無しさん@お腹いっぱい。
2009/01/13(火) 21:12:10正規表現中の「'」がうまく認識されるのですか?
awk '
$0 !~ /^'
ここでぶったぎられそうな気がするのですが。
0645名無しさん@お腹いっぱい。
2009/01/13(火) 21:23:35ぶったぎられる、というのはその通り。
ところが、その直後にスペースを入れずに "$searchLine" と続けているから、
結局これら全体がシェル上で「1つの引数」になって awkに渡される。
0646名無しさん@お腹いっぱい。
2009/01/13(火) 22:38:22単にシェルが「'」を食べるだけ。
#!/usr/bin/awk -f というスクリプトを考えたら良い。
そう言う場合は「'」は不要でしょう?。
要するに、「'」と「'」に挟まれたところをシェルがそのまま awk に渡し、
その外側の部分を、シェルが先に解釈(展開)すると言うだけの事。
最終的には全部 awk に渡される。
0647名無しさん@お腹いっぱい。
2009/01/13(火) 22:44:06違うよ。
>>645
が正解。
0648名無しさん@お腹いっぱい。
2009/01/13(火) 23:13:01$set 'Path'" $HOME "' is my home.'
$ echo $1
Path /home/foo is my home.
##
$ set 'Path' "$HOME" ' is my home.'
$ echo $1
Path
$ echo $2
/home/foo
$ echo $3
is my home.
こういうことだけど。
文字列の間に空白がないから一つになってる。
0649名無しさん@お腹いっぱい。
2009/01/13(火) 23:14:10むむ。
>>645 は、何も言っていないに等しいとしか読めない。
スペースの有る無しは変数名が変わると言うことだし、
awk への引数ではなく、その文脈上の展開した値として単純に awk に渡されると
認識している。
念を押すが、awk (にしろ他のコマンドにしろ)「'」の存在は知らない。
0650名無しさん@お腹いっぱい。
2009/01/13(火) 23:17:03??
0651名無しさん@お腹いっぱい。
2009/01/13(火) 23:28:32なるほど。ありがと。
0652名無しさん@お腹いっぱい。
2009/01/14(水) 03:13:11ペタってコード書ける言語って、
他になにがありますかね。
■ このスレッドは過去ログ倉庫に格納されています