質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

Q&A

解決済

2回答

2199閲覧

シェルスクリプト while readで読む行を途中で変更したい。

decman

総合スコア1

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

ファイル

ファイルとは、文字列に基づいた名前又はパスからアクセスすることができる、任意の情報のブロック又は情報を格納するためのリソースです。

while

Whileは多くの言語で使われるコントロール構造であり、特定の条件が満たされる限り一連の命令を繰り返し実行します。

0グッド

0クリップ

投稿2021/12/06 13:13

編集2021/12/06 15:42

シェルスクリプトにおけるwhile readについて相談させてください。

やりたいこと

while readにおいて、読みだしてくるファイルを適宜削除し
読み出すか読みださないかを変更できるようにしたいです。

以下の例でいうと、テキストファイルtest.txt内の「cccccc」をechoで表示させないようにする認識です。
main.shとtest.txtの内容はできるだけ変更せずに実装したいです。

■main.sh while read line do   if [ `echo $line | awk '{print $1}' ` == "bash" ];then eval "$line" else echo $line  fi done < test.txt
■ChangeFile.sh LINE=`grep -n "cccccc" test.txt | awk '{print $1}' | sed s/[^0-9]//g` sed -i "${LINE}d" test.txt TEST=$(<test.txt) echo $TEST
■test.txt aaaaaa bbbbbb bash ChangeFile.sh cccccc cddddd

現状

以下実行結果の通りです。

■実行結果 aaaaaa bbbbbb aaaaaa bbbbbb bash ChangeFile.sh dddddd cccccc dddddd

「cccccc」が出力されないようにしたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/12/06 13:19

> main.shとtest.txtの内容はできるだけ変更せずに実装したいです。 main.shの中身自体が不自然な印象があるのですが、このような構成を取る理由はどのようなものでしょうか?
decman

2021/12/06 13:21

すみません、一部誤記があったため修正しました。
otn

2021/12/06 14:36

main.shが実際に実行しているものでは無いですね。コピペくらい間違わずやりましょう。
decman

2021/12/06 15:35

申し訳ありません、コピペできない場で開発していたものの一部を内容変更して記載しているため 内容が誤っておりました。 再度内容を修正しました。確認をお願いします。
guest

回答2

0

ed コマンドを使う方法もあります。

ChangeFile.sh

bash

1 2#LINE=$(grep -n "cccccc" test.txt | cut -d: -f1) 3 4match='cccccc' 5ed test.txt <<EOS >/dev/null 2>&1 6/${match}/d 7wq 8EOS 9 10TEST=$(<test.txt) 11echo $TEST

なお、main.sh は以下の様に書き換えても良いかと思います。

main.sh

bash

1#!/bin/bash 2 3while read -r line 4do 5 set -- $line 6 if [ $1 == bash ];then 7 eval "$line" 8 else 9 echo "$line" 10 fi 11done < test.txt

投稿2021/12/06 16:54

編集2021/12/06 17:57
melian

総合スコア20655

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2021/12/07 11:44

なるほど。wqだと同じファイルを書き換えますね。 今時は ed はインストールされてないかもしれないので、exが良いかもしれません。
guest

0

ベストアンサー

ファイルを読んでいる途中でそのファイルを書き換えたいと言うことですね。

sedの種類によるかもしれませんが、少なくともGNU sedでは、-iオプションは、別ファイルに書きだした後でリネームで元のファイル名を付けます。
例えば、sed -i 's/a/b/' foo.txtは、sed 's/a/b/ foo.txt > tmp;rm foo.txt;mv tmp foo.txtと同じです(必要に応じてパーミッションなどの変更も)。

つまり、元のファイルを書き換えませんので、そのファイルを読んでいる別のプロセス(この場合はmain.sh)には影響しません。

ということで、-iを使わず、元のファイルをcpで上書きすればいいです。

sh

1■ChangeFile.sh 2 3LINE=`grep -n "cccccc" test.txt | awk '{print $1}' | sed s/[^0-9]//g` 4 5sed "${LINE}d" test.txt > tmp 6cp tmp test.txt 7rm tmp 8 9TEST=$(<test.txt) 10echo $TEST

投稿2021/12/06 16:17

編集2021/12/06 16:52
otn

総合スコア85901

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2021/12/06 16:29

一時ファイルはわかりやすさのために tmp と書いてますが、 /var/tmp/$$ などの名前にするのが良いでしょう。
decman

2021/12/06 16:39

粗雑な質問にもかかわらず丁寧な回答をして頂き、本当にありがとうございます! sedの-iに関する説明について調べたところ「ファイルを直接編集する」というような記載があったため、 内部的にはファイルを直接書き換えるわけではないという認識がありませんでした。 これからは、コマンドが内部的にどのような動きをしているのかについて気にするようにします。 ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問