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

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

新規登録して質問してみよう
ただいま回答率
85.47%
シェル

シェル(shell)はUnix や Linux 系のOSで使用されるコマンドインタプリタを指します。

Q&A

解決済

5回答

931閲覧

シェルスクリプト 1つの行の中に同じファイルパスが複数書かれているテキストからファイルパス文字列を取得したい

rasum

総合スコア21

シェル

シェル(shell)はUnix や Linux 系のOSで使用されるコマンドインタプリタを指します。

0グッド

0クリップ

投稿2022/01/02 13:43

シェルスクリプト(bash)にて、1つの行の中に同じファイルパスが複数書かれている下記のsample.txtから、各行のファイルパス文字列を取り出そうとしています。
とりあえず最初の行から取り出すファイルパスを取り出す方法をお教え下さい。

$ cat sample.txt 1,/data/aa.ext,641,/data/aa.ext, 2,/data/bbb.ex,830,/data/bbb.ex, 2,/data/cc.ex1,830,/data/cc.ex1 (できない例) $ cat sample.txt | grep -Eo -m 1 "/data/.*.?[a-zA-Z0-9]*" /data/aa.ext,641,/data/aa.ext, $ cat sample.txt | grep -Po -m 1 "/data/.*.?[a-zA-Z0-9]*" /data/aa.ext,641,/data/aa.ext, $ cat sample.txt | grep -Po -m 1 "/data/.*.[a-zA-Z0-9]*?," /data/aa.ext,641,/data/aa.ext,

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

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

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

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

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

guest

回答5

0

1つの行の中に同じファイルパスが複数書かれている下記のsample.txtから、

「複数書かれている」というのが重要であるなら、
/で始まる文字列が2つ同じ物が出てきたら2つめを抜き出す」だと、

sh

1grep -P -o '(/.*).*\K\1'

ですね。ただこれだと、,もパスの一部と見なすので、含めたくないなら、

sh

1grep -P -o '(/[^,]*).*\K\1'

\1はその前の括弧内と同じ文字列。\Kは後読みという概念で、マッチした後で、そこから前の部分はマッチした文字列から(つまり-oでの表示対象から)除きます。

実行結果:

sh

1cat <<EOF | 21,/data/aa.ext,641,/data/aa.ext, 32,/data/bbb.ex,830,/data/bbb.ex, 42,/data/cc.ex1,830,/data/cc.ex1 5EOF 6grep -P -o '(/[^,]*).*\K\1' 7===> 8/data/aa.ext 9/data/bbb.ex 10/data/cc.ex1

投稿2022/01/02 15:33

編集2022/01/02 15:38
otn

総合スコア84710

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

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

rasum

2022/01/03 10:00

otn様、ありがとうございます。 正しく動作することを確認しましたが、私には、'(/.*).*\K\1'が次のように動作するように思われるのですが、正しい動作の仕組みをお教え下されば助かります。 「a. (/.*)によって/で始まる文字列の末尾までがマッチする」 「b. 続く .* に対する肯定後読みで、aでのマッチの最後尾の後ろ(=文字列の末尾)がマッチポイントとなる」 「c. bでのマッチポイントから後ろには何もないので、\1 にマッチすることはない 」
otn

2022/01/03 10:32

\Kは後から考えれば良いので、さておき、 (/.*).*\1 は、「/で始まる文字列 甲 があり、そのあとで甲と同じ物が出てくる」という文字列にマッチします。 最初の (/.*) が文字列全体にマッチさせた場合、\1 にマッチする物がないので、全体としてマッチが失敗しますので、そういう結果は出てきません。 具体例で言うと、対象が、 /abc/ab だとすると、全体のマッチで (/.*) になり得るのが、/ と /a と /ab だけですが、その中の最長マッチである /ab が該当します。 (/.*) が /abc にマッチした場合は、\1 にマッチする物がないので全体のマッチが失敗します。
rasum

2022/01/03 14:31

> (/.*).*\1 > は、「/で始まる文字列 甲 があり、そのあとで甲と同じ物が出てくる」という文字列にマッチします。 上記が理解できないのですが、「丸括弧のグループ化部分」「.*での文字の連続の部分」「\1 の置換の部分」と分けて考えるのではなく、全体でこういうものと理解すべきものなのでしょうか?
otn

2022/01/03 15:43

ちょっと質問の意味がわかりません。 その3つの正規表現が連結して1つになった物が、回答の正規表現です。 \1は置換じゃないですが?後方参照と呼ばれます。
otn

2022/01/03 15:54 編集

「上記が理解できない」というのが理解できないのですが、 a.*b が「aで始まる文字列がありそのあとでbが出てくる」というのも理解できませんか? 理解できるなら、それと同じ構造なのですが。
rasum

2022/01/03 16:14

\1は置換ではなく参照ですね。間違えました。 (/.*).* において、(/.*)でキャッチする範囲は「\1 が成立する範囲で、その範囲の後ろから \1 の直前までは(/.*)の後の .* にマッチする」ということでしょうか? (/.*)で文字列の末尾までをキャッチしてしまうように思えてしまいます。
otn

2022/01/03 16:28 編集

> (/.*)で文字列の末尾までをキャッチしてしまうように思えてしまいます。 のように感じられたので、 > a.*b が「aで始まる文字列がありそのあとでbが出てくる」というのも理解できませんか? と聞いてみました。a.* が文字列の最後までマッチしたら、bにマッチする物がなくなって、axbにもマッチしません。
rasum

2022/01/04 01:05

a,*b は「bが末尾だよ」と示していますが、/.* は「/で始まったら最後まで全部だよ」と言っているように思ってしまいます。 その上で、(/.*).*\1 は、「/で始まり文字列の最後までのものから \1 でマッチする部分を( )グループの文字列とし、残りの文字列は( )と\1の間の .* にマッチさせている」と理解するのは間違いなんでしょうね。
otn

2022/01/04 01:35

はい。間違いです。 .* の後にまだ続きがある場合は、「最後まで全部」じゃなくて、「続きの手前まで」にマッチします。 a.*bと同じです。
rasum

2022/01/04 02:08

"1,/data/aa.ext,641,/data/aa.ext,"を対象文字列とした時、 (/.*).* で「続きの手前まで」のマッチはどこまでなのでしょうか?
otn

2022/01/04 10:55

どっちの .* のことですかね? 全体の正規表現が (/.*).*\1 だとすると、 1つめの .* の続きは .*\1 で、これは 641,/data/aa.exe, にマッチするのでその手前までです。 2つめの .* の続きは \1 で、これは/data/aa.exe,にマッチするのでその手前までです。
otn

2022/01/04 11:14

マッチするまでのプロセスが理解できないと言うことですかね? "1,/data/aa.ext,641,/data/aa.ext," と (/.*).*\1 だとして、 まず(/.*)の繰り返しが0回つまり、/ のとき、\1も/なので、正規表現は /.*/ となり、これは文字列 /data/aa.ext,641,/data/ にマッチします。中間の.*が最長マッチなので、data/aa.ext,641,/data ですね。 全体のマッチが成功したので、もし (/.*?).*\1 というふうに最小マッチならこれで終わりです。 ところが最長マッチなので、より長い .* でもマッチするかもしれないのでチェックします。 .*が1回の繰り返し、つまり、(/.*) が /d のとき、\1 も /d なので、正規表現は /d.*/d になりますが、これは /data/aa.ext,641,/d にマッチしますね。 (中略) .*が12回の繰り返し、つまり (/.*) が /data/aa.ext, のとき \1 も /data/aa.ext, なので、正規表現は、/data/aa.ext,641,/data/aa.ext, にマッチします。 .*が13回の繰り返し、つまり (/.*) が /data/aa.ext,6 のとき \1 も /data/aa.ext,6 になりますが、これは対象文字列にマッチしません。 最長マッチはマッチする範囲での最大回数なので、12回繰り返しと言うことで決定しました。 今、0回繰り返しからマッチしなくなるまで伸ばしていきましたが、最大マッチなので最大からマッチするまで縮める方法もあると思います。実際の正規表現ライブラリがどういうアルゴリズムなのかは知りません。
rasum

2022/01/04 13:43

詳しい説明をありがとうございます。やっと、参照がある時のマッチの仕組みがわかりした。
guest

0

私ならawkを使います。

echo ',/data/aa.ext,641,/data/aa.ext' | awk -F, '{p[$2]=1; p[$4]=1; for(k in p){print k}}'

投稿2022/01/02 15:31

KojiDoi

総合スコア13671

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

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

0

ベストアンサー

要件が不明確ですが、とりあえず

$ grep -o '/data/[^,]*' sample.txt /data/aa.ext /data/aa.ext /data/bbb.ex /data/bbb.ex /data/cc.ex1 /data/cc.ex1 $ grep -o '/data/[^,]*' sample.txt | uniq /data/aa.ext /data/bbb.ex /data/cc.ex1

投稿2022/01/02 14:37

itagagaki

総合スコア8402

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

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

rasum

2022/01/03 03:08

itagagaki様、ありがとうございます。 大変シンプルでわかりやすい方法をありがとうございます。
guest

0

bash

1$ cat sample.txt | grep -Po '/data/.*?((?=,)|$)' 2/data/aa.ext 3/data/aa.ext 4/data/bbb.ex 5/data/bbb.ex 6/data/cc.ex1 7/data/cc.ex1

投稿2022/01/02 14:19

melian

総合スコア19849

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

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

rasum

2022/01/03 03:04

melian様、ありがとうございます。 正規表現におけるグループ化、先読みの勉強になりました。
guest

0

とりあえず

bash

1cat sample.txt | \ 2while IFS=, read a b c d e 3do 4 echo $b 5 echo $d 6done

--- 追記 ---

bash

1cat sample.txt | cut -f 2 -d,

投稿2022/01/02 14:10

編集2022/01/04 11:49
takasima20

総合スコア7460

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

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

rasum

2022/01/03 02:54

takasima20様、ありがとうございます。 元データが大きいとreadの引数に困るかと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問