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

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

新規登録して質問してみよう
ただいま回答率
85.35%
正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

解決済

4回答

2192閲覧

正規表現が解読できません。

cunwe

総合スコア65

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

0グッド

0クリップ

投稿2020/09/25 03:51

前のパソコンが壊れてしまったので新しいパソコンで競プロの環境構築をしようと、こちらの記事を参考に進めていたのですが、記事内でシェルスクリプトに

#!/bin/sh file = $1 objfile = `echo $f | sed 's/.[^.]*$//'` g++ -g -o $objfile $file ./$objfile

と書いていたのですがs/.[^.]*$//がどういう意味なのか気になったので正規表現を調べてこちらの記事に出会いました。しかし、あまり理解できませんでした。[]の中の^は"行頭"ではなくて"否定"を表す、文字としての"."の前には""、ということから[^.]で""."以外"ということしか分からなくてそれも意味不明でなかなか全体の解読に至れないのでs/.[^.]*$//の意味を丁寧に解説していただける方がいらっしゃいましたらよろしくお願いいたします。

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

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

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

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

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

DreamTheater

2020/09/25 04:14 編集

最後の $// ってタイプミスではないのですか? スラッシュが1個余計で、$/ が正しいのでは?
kuma_kuma_

2020/09/25 04:17

正規構文の中に$1の値が代入されて...のはずなんですが それなら's/\.[^\.]*$1//'`だと思うんですが 翻訳しても文字列として変なんですよね... '/\.[^\.]*/\.$1'`ならまだ判るんですが。(と言ってもフルパスじゃないし...)
Daregada

2020/09/25 04:20

これは正規表現単独ではなく、sedに与えるコマンドなので、末尾の//はタイプミスではありません。s/置換前/置換後/ という指定ね。
DreamTheater

2020/09/25 04:22

Qiitaのページ見ると確かに$//で終わってますね。。。 あ!置換ですね、分かりました回答します。
guest

回答4

0

ええと、その正規表現でやっていることは、「文字列の末尾にあるファイル名拡張子(.XXXみたいなやつ)を取り除く」という処理ですね。

ただ、シェルスクリプトが提示されたサイトに載っている時点で間違っているので、まずは以下のように修正してください。=の両側に空白があると、シェル変数への代入と見なされず、うまく動きません。それから、fが突然出てきますが、fileの書き間違いですね。

bash

1#!/bin/sh 2file=$1 3objfile=`echo $file | sed 's/.[^.]*$//'` 4 5g++ -g -o $objfile $file 6./$objfile

シェル変数fileには、シェルスクリプト実行時の最初のコマンドライン引数を$1で取得して設定しており、提示されたサイトの流れでは、g++で処理したいファイル名が格納されます。

echoでそのファイル名を出力し、それをsedのsコマンドで一部を置換し、処理結果の文字列をコマンド置換でobjfileに格納します。

肝心のsedの処理ですが、「.で始まり、.以外の文字が(0文字以上)並んで、文字列末尾に至る」部分を見つけ、もしそれがあればその部分を空文字列に置換(つまりは削除)しています。例えば、「hogehoge.cpp」なら、「.cpp」の部分を取り除いて、「hogehoge」という文字列を生成しています。

.は「文字通りの.(ドット、ピリオド)」を示すパターンですね。そのまま.と書くと、正規表現では「任意の1文字」を示す特殊な記号(メタ文字)として扱われるので、.と書く必要があります。

それに続く[^.]は、「.以外の任意の文字」を示すパターンです。[^……]は、内部に書いた文字を除いた任意の1文字にマッチします。なお、[]の内部では.は「文字通りの.」を意味するので、提示したサイトのシェルスクリプトのように.と書く必要はありません。.と書いても同じ意味になりますけどね。

その直後の*は、「直前のパターンの0回以上の繰り返し」を意味します。なお、0回というのは「一度も現われない」ことを示します。1回、2回、……はいいですよね。直前のパターンが[^.]なので、[^.]*が「.以外の任意の文字の0文字以上の繰り返し」を示すパターンになります。

最後の$は、「文字列の終わり」という位置を示すパターンです(厳密には違うのですが、sedは行単位でしか正規表現を扱わないので詳細は省略)。

つまり、全体としては、「どこから始まっていてもいいが、.で始まり、.以外の文字が文字列の終わりまで並んでいたら、その.を含めて末尾まで」がマッチします。

sedのsコマンドは、s/パターン/置換文字列/フラグ(省略可)という形式で、パターンにマッチした部分を置換文字列に置き換えた文字列を出力します。今回は置換文字列が空文字列なので、パターンにマッチした部分が削除された文字列が生成されるわけです。

投稿2020/09/25 04:53

編集2020/09/25 04:58
Daregada

総合スコア11990

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

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

cunwe

2020/09/25 12:19

最後の//は/と/の間がない、つまり削除を表しているということだったのですね。シェルスクリプトの書き方まで教えてくださりありがとうございました!
guest

0

「s/.[^.]$//」はsedの置換コマンドで「s/(置換対象の正規表現)/(置換後の文字列)/」の形になっています。
置換対象の正規表現が「.[^.]
$」で、置換後の文字列が無いので削除を表します。

置換対象の正規表現「.[^.]$」は「.」と「[^.]」と「$」に分けられます。さらに「[^.]」は「^.]」と「」に分けられます。

  • 「.」は「.」という文字を表します。(「.」と書いてしまうと任意の1文字という別の意味になってしまいます。「.」という文字そのものを表すには「.」とします)

  • 「[^.]*」の「[^.]」は「.」という文字以外の文字の1字を表します。(例えば「[ABC]」は「A」「B」「C」のいずれかにマッチします。[^ABC]は「A」「B」「C」でない文字の1字にマッチします。)

  • 「[^.]」の「」は直前に指定された正規表現の0回以上の繰り返しです。(例えば「あ*」は「」「あ」「ああ」「あああ」「ああああああああ」などにマッチします。0回以上の繰り返しなので「あ」の0回の繰り返しである「」にもマッチします。)

  • 「$」は文字列の最後にマッチします。(例えば「xyz$」という正規表現は「vwxyz」にはマッチしますが「xyza」にはマッチしません)

つまり「.[^.]$」という正規表現を日本語で表現すると、.(なんとか)で終わる文字列、を表します。
「s/.[^.]
$//」というsedコマンドは、.(なんとか)で終わる文字列を削除するコマンド、となります。

あと(なんとか)の部分が何故「.」ではなく「[^.]」という回りくどい表現になっているかというと、「.」という文字を含めてしまうと「.」が複数ある場合になるべく長い文字列に一致しようとしてしまうためこれを避けるためだと思われます。(このマッチングの仕方を最長一致と言い大抵の正規表現でのデフォルトの挙動となっています。)

$ cat hoge.txt aaa.txt bbb.txt ccc.ccc.txt ddd.txt. $ sed 's/.[^.]*$//' hoge.txt aaa bbb ccc.ccc ddd.txt $ sed 's/..*$//' hoge.txt aaa bbb ccc ddd

投稿2020/09/25 04:44

編集2020/09/25 05:04
hidezzz

総合スコア1248

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

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

cunwe

2020/09/25 12:23

実例を用いて教えてくださりありがとうございます!
guest

0

こんにちは。

正規表現説明
s/正規表現の始まりです(誤記削除)
s置換を意味します(追記)
/正規表現の始まりです(追記)
.ドット1文字を表します
[^.]*ドット以外の文字が0文字以上繰り返す
$行末を表す
/正規表現(検索条件)の終わりです
/正規表現(置換文字列)の終わりです

以上から全体としては
・行末のドット
・ドットの直後にドット以外の文字が1文字以上行末まで続く文字列
上記いずれかにマッチ文字列を空にする(=削除する)正規表現です。

No置換前置換後説明
1hoge.hoge行末のドットが削除される
2hoge.fugahoge.fugaが削除される
3hoge.fuga.wawahoge.fuga.wawaが削除される
4hoge..fugahoge..fugaが削除される
5hogefugahogefuga何も変わらない

こちらのサイトで正規表現を図式化してくれますのでご参考まで。

投稿2020/09/25 04:34

編集2020/09/25 04:44
DreamTheater

総合スコア1095

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

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

Zuishin

2020/09/25 04:40

> 正規表現の始まりです s は正規表現ではなく置換コマンドです。
DreamTheater

2020/09/25 04:43

訂正しました。 フォローありがとうございます。
cunwe

2020/09/25 12:24

分解してひとつひとつ教えてくださりありがとうございます!
guest

0

ベストアンサー

こちらの場合、正規表現だけでなくsed独自の記述も含まれているのでちょっと面倒ですね。

まずs/はsedの正規表現置換であるsコマンドを使うことを宣言しています。
次に.は.が任意の一文字を表すメタ文字をエスケープし、単純に.(ピリオド)の文字として指定しています。
そして[^.]は質問者さんの解釈通り、.(ピリオド)でないこと、そして続く*がそのマッチ条件の0回以上の繰り返しなので、
.でない文字が連続で続き、$が末尾文字を表しているので、
この正規表現を総合的に解釈すると文字列の最後の.(ピリオド)から末尾までの.以外の文字にマッチします。

そして$の次の/はsedのsコマンドのマッチ文字と置換文字の境界を示し、
その次に何も指定していないので、
上記のマッチした「文字列の最後の.(ピリオド)から末尾まで.以外の文字」を空文字に置換する。

つまり単純な解釈で言うとファイル名の拡張子を切り捨てるためのsedコマンドでの正規表現置換なのではないかと思います。

ちょっと取り留めもない説明になっていますがわかりますでしょうか。

投稿2020/09/25 04:34

yureighost

総合スコア2183

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

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

cunwe

2020/09/25 12:17

はい、お陰さまで理解できました。ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問