シェルスクリプトによってファイルを書き換えます。
- sedコマンドの概要
- 具体例一覧
sedコマンドの概要
対象とするファイル名”input.ini”の中身に
a=7,
b=13,
c='./dat/',
&end
という文が書いてあるとします。
これのファイルの中身を端末からのコマンドで書き換えるためにはsedコマンドを利用します。
sedコマンドとは、置換用のコマンドです。
例えば、コマンドライン上で
と入力すると”input.ini”の中身の”a=7″に一致する場所を”a=9″に置換して出力します。
ここで重要なのが、”a=7″に一致する場所だけなので、後ろにあるコンマは影響されません。
実際に実行してみると
&input
a=9,
b=13,
c='./dat/',
&end
となってコマンドライン上に出力されます。
この時、書き換わった内容はコマンドラインのみに出力されるため、元のファイル自体に書き換えはされません。
元のファイルの書き換えを行いたければ、出力先を適当なファイル、例えば”tmp”というファイルにし、それを”input.ini”にリネームすればいいです。
すなわち、
mv tmp input.ini
とすればいいのです。実際に実行して中身を見ますと
$ mv tmp input.ini
$ cat input.ini
&input
a=9,
b=13,
c='./dat/',
&end
となります。
sedコマンドの概要はここで終了です。
以降は具体例とシェルスクリプトのコード(ファイル名”q.sh”),実行例を載せていきます。
特定の数値を変えたい場合
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
sed -e "s/a=7/a=9/" ./input.ini > tmp
mv tmp input.ini
実行例
$ cat input.ini
&input
a=9,
b=13,
c='./dat/',
&end
特殊文字(/等)を含む文字列の変更
sedコマンドを使うために/を使います。ならば/を置換したい場合どうすればいいでしょう。
この時はsedコマンドで使っている区切りを表す/を別の特殊文字(例えば%)に置き換えればよいです。
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
sed -e "s%c='./dat/'%c='./data/'%" input.ini > tmp
mv tmp input.ini
実行例
$ cat input.ini
&input
a=7,
b=13,
c='./data/',
&end
置換する数値の変数での指定
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
pr=5
sed -e "s/a=7/a=${pr}/" ./input.ini > tmp
mv tmp input.ini
実行例
$ cat input.ini
&input
a=5,
b=13,
c='./dat/',
&ends
ある数値以降を全て変更したい
コンマも含めて消したいとします。
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
sed -e "s/c=.*/c=12345/" ./input.ini > tmp
mv tmp input.ini
実行例
$ cat input.ini
&input
a=7,
b=13,
c=12345
&end
ファイルの空白行の削除
ファイルに含まれる空白行を全て削除します。
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
sed -e '/^ *$/d' input.ini > tmp
mv tmp input.ini
実行例
$ cat input.ini
&input
a=7,
b=13,
c='./dat/',
&end
ファイルの行頭に数値を入れる
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
i=1
while [ ${i} -le 1000 ]
do
sed -e "${i}s/^/${i} /" input.ini > temp
mv temp input.ini
i=`expr $i + 1`
done
実行例
$ cat input.ini
1 &input
2 a=7,
3 b=13,
4 c='./dat/',
5 &end
6
“q.sh”中、1000をファイルに応じて変更してください。
※また、行番号を入れるだけの場合、catコマンドを利用して、
mv tmp input.ini
とするのが簡単だと思います。
オプション”-b”は空行を含めず行番号を付けるという意味です。空行を含めたい場合は”-n”でできます。
ファイルの最後に改行を入れる
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
sed '$a\\n' input.ini > tmp
mv tmp input.ini
実行例
$ cat input.ini
&input
a=7,
b=13,
c='./dat/',
&end
$
※どうしても2行分改行されてしまうようです。
sedコマンドで1行分の改行は、どうすればよいか分かりませんでした。
苦肉の策ですが、適当なファイル(例えばnn.txt)に改行だけを入力し、catコマンドでファイルを結合すると一応できます。
—nn.txt—
————
を作っておいて、
#!/bin/sh
cat input.ini nn.txt > tmp
mv tmp input.ini
とすれば”input.ini”の後ろに”nn.txt”が結合されるため、1行分の改行が可能です。
do文で何回も変更したい
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
for pr in 0 1 2
do
sed -e "s/a=.*/a=${pr},/" input.ini > tmp
mv tmp input.ini
cat input.ini
done
実行例
&input
a=0,
b=13,
c='./dat/',
&end
&input
a=1,
b=13,
c='./dat/',
&end
&input
a=2,
b=13,
c='./dat/',
&end
全く違う変数を繰り返し代入したい
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
i=1
for j in 3d0 939d0
do
echo "count -> ${i}"
sed -e "s/a=.*/a=${j},/" ./input.ini > tmp
mv tmp input.ini
grep "a=" input.ini
i=`expr $i + 1`
done
実行例
count -> 1
a=3d0,
count -> 2
a=939d0,
$
シェルスクリプトの中の”grep”は”a=”に一致する行を書き出させるものです。
整数の変数を規則的に何回も変更したい
対象とするファイル”input.ini”
a=7,
b=13,
c='./dat/',
&end
“q.sh”の中身
i=1
while [ ${i} -le 3 ]
do
echo "count -> ${i}"
sed -e "s/a=.*/a=${i},/" ./input.ini > tmp
mv tmp input.ini
grep "a=" input.ini
i=`expr $i + 1`
done
実行例
count -> 1
a=1,
count -> 2
a=2,
count -> 3
a=3,
$
小数点を含む変数を規則的に何回も変更したい
小数点を含む演算は”expr”コマンドではできません。
シェルスクリプトで数値計算を行う”bc”コマンドを用いましょう。
ここでは、変数\(v\)に、
\(v=i\times0.7,\;\;(i=1,2,3)\)
を代入する演算を考えましょう。
対象とするファイル”input.ini”
a=3,
b=13,
c='./dat/',
&end
“q.sh”の中身
i=1
while [ ${i} -le 3 ]
do
echo "count -> ${i}"
v=$(echo "scale=3; $i*0.70 " | bc | sed -e 's/^\./0./g')
echo "$v"
sed -e "s/a=.*/a=${v},/" ./input.ini > tmp
mv tmp input.ini
grep "a=" input.ini
i=`expr $i + 1`
done
実行例
count -> 1
0.70
a=0.70,
count -> 2
1.40
a=1.40,
count -> 3
2.10
a=2.10,
$
あるディレクトリ以下にある全ファイル名を変数にしたい
状況
├── a.sh
├── dat
├── data
│ ├── asdfgh.d
│ ├── qwerty.d
│ └── zxcvbn.d
└── input
inputの中身
filename="./data/xxxxx.d",
key = 0,
&end
a.shの中身
i=1
for j in ./data/*
do
sed -e "s%filename=.*%filename="${j}",%" input > tmp
mv tmp ./dat/${j##*/}_ex
echo ${j##*/}
grep "filename=" ./dat/${j##*/}_ex
done
———————-
a.sh実行
asdfgh.d
filename="./data/asdfgh.d",
qwerty.d
filename="./data/qwerty.d",
zxcvbn.d
filename="./data/zxcvbn.d",
a.sh実行後の構成
├── a.sh
├── dat
│ ├── asdfgh.d_ex
│ ├── qwerty.d_ex
│ └── zxcvbn.d_ex
├── data
│ ├── asdfgh.d
│ ├── qwerty.d
│ └── zxcvbn.d
└── input
例えばdat/asdfgh.d_exの中身
filename="./data/asdfgh.d",
key = 0,
&end
参考先
フィルタを使用した文字列操作 1 -UNIX & Linux コマンド・シェルスクリプト リファレンス
sed の使い方 -サーバエンジニアの知恵袋
制御構文 -シェルスクリプト入門
How to pass results of bc to a variable – Ask Ubuntu
bcによる少数の演算 -SWEng TIPs