テキストファイル内の一部のテキストを置換する必要があります。通常私は次のようなことをします
sed -i 's/text/replacement/g' path/to/the/file
問題は、text
とreplacement
の両方がダッシュ、スラッシュ、ブラックスラッシュ、引用符などを含む複雑な文字列であることです。 text
内の必要なすべての文字をエスケープすると、すぐに判読できなくなります。一方、正規表現の力は必要ありません。テキストを文字どおりに置き換えるだけです。
テキスト置換を行う方法はありますかなしいくつかのbashコマンドで正規表現を使用していますか?
これを行うスクリプトを作成するのは簡単ですが、既に何かが存在しているはずです。
正規表現の力が必要ない場合は、使用しないでください。それは結構です。
しかし、これは実際には 正規表現 ではありません。
sed 's|literal_pattern|replacement_string|g'
したがって、/
が問題である場合は、|
を使用すれば、前者をエスケープする必要はありません。
PS:コメントについては、このStackoverflowの回答 sed検索パターンの文字列をエスケープする も参照してください。
Update:Perlを使用して問題がない場合は、\Q
と\E
はこのように、
Perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
@RedGrittyBrickは、コメントでより強力なPerl構文を使用して同様のトリックを提案しました here または here
export FIND='find this'
export REPLACE='replace with this'
Ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file
これは、ここで唯一の100%安全なソリューションです。
sed
を使用するよりも優れています)}
charが含まれていても壊れません(したがって、送信されたPerlソリューションより優れています)ENV['FIND']
ではなく$FIND
が使用されるため、どの文字でも壊れません。 $FIND
またはRubyコードにインライン化されたテキストを使用すると、エスケープされていない'
が文字列に含まれていると、構文エラーが発生する可能性があります。replace
コマンドはこれを行います。
https://linux.die.net/man/1/replace
場所の変更:
replace text replacement -- path/to/the/file
Stdoutへ:
replace text replacement < path/to/the/file
例:
$ replace '.*' '[^a-z ]{1,3}' <<EOF
> r1: /.*/g
> r2: /.*/gi
> EOF
r1: /[^a-z ]{1,3}/g
r2: /[^a-z ]{1,3}/gi
replace
コマンドはMySQLまたはMariaDBに付属しています。
また、Perlの\Q
メカニズムを使用して、「 引用(無効にする)パターンのメタ文字
Perl -pe 'BEGIN {$text = q{your */text/?goes"here"}} s/\Q$text\E/replacement/g'
私のPerlスクリプトをチェックしてください。正規表現を暗黙的または明示的に使用せずに、必要なことを正確に実行します。
https://github.com/Samer-Al-iraqi/Linux-str_replace
str_replace Search Replace File # replace in File in place
STDIN | str_replace Search Replace # to STDOUT
とても便利ですよね?それを行うには、Perlを学ぶ必要がありました。本当に必要だからです.
あなたのパターンをエスケープすることでそれを行うことができます。このような:
keyword_raw='1/2/3'
keyword_regexp="$(printf '%s' "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g')"
# keyword_regexp is now '1\/2\/3'
replacement_raw='2/3/4'
replacement_regexp="$(printf '%s' "$replacement_raw" | sed -e 's/[\/&]/\\&/g')"
# replacement_regexp is now '2\/3\/4'
echo 'a/b/c/1/2/3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# the last command will print 'a/b/c/2/3/4/d/e/f'
このソリューションのクレジットはここにあります: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern
注1:空ではないキーワードに対してのみ機能します。空のキーワードはsed(sed -e 's//replacement/'
)。
注2:残念ながら、問題を解決するためにregexp-sを使用しない一般的なツールはわかりません。このようなツールはRustまたはCで記述できますが、デフォルトではありません。
私は他のいくつかの答えをつなぎ合わせてこれを思いつきました:
function unregex {
# This is a function because dealing with quotes is a pain.
# http://stackoverflow.com/a/2705678/120999
sed -e 's/[]\/()$*.^|[]/\\&/g' <<< "$1"
}
function fsed {
local find=$(unregex "$1")
local replace=$(unregex "$2")
shift 2
# sed -i is only supported in GNU sed.
#sed -i "s/$find/$replace/g" "$@"
Perl -p -i -e "s/$find/$replace/g" "$@"
}
これをスクリプトなしで実行できます(ただし、この「ワンライナー」をスクリプトに追加する方がよいでしょう)または非標準の外部プログラム(@Nowakerの answer に対する安全性のおかげで、インジェクションですが、私がこれを必要としていたこの古いCentOSボックスにはRubyがありませんでした!)。 Perl
があなたにとって非標準でない限り
文字列をescapeエスケープせずに(そして、すべての特殊文字を知っているなど、構文的に正しく実行することによる問題を考慮せずに)、単にブランケットすることができます。すべての文字列をエンコードして、特別である可能性がないようにします。
_cat path/to/the/file | xxd -p | tr -d '\n' \
| Perl -pe "s/$(printf '%s' 'text' | xxd -p | tr -d '\n')(?=(?:.{2})*\$)/$(printf '%s' 'replacement' | xxd -p | tr -d '\n')/g" \
| xxd -p -r
_
これは単に質問者の例と一致させるためでした。他のユーザーは、変数を使用する場合は_'text'
_を_"$text"
_に、ファイルを使用しない場合は_cat path/to/the/file
_を_printf '%s' "$input"
_に置き換えることができます。
_/g
_を_/
_で置き換えて1回置換するか、または$()
の外側の正規表現を編集して、マッチャーの一部のみを「エスケープ」することもできます(たとえば、 _^
_の後に_s/
_を追加して、ファイル)の先頭のみに一致させる.
上記の場合、^/$でends-of-linesに一致させる必要がある場合は、再度エンコード解除する必要があります。
_cat path/to/the/file | xxd -p | tr -d '\n' | sed 's/0a/\n/g'\
| Perl -pe "s/^$(printf '%s' 'text' | xxd -p | tr -d '\n')(?=(?:.{2})*\$)/$(printf '%s' 'replacement' | xxd -p | tr -d '\n')/g" \
| sed 's/\n/0a/g' | xxd -p -r
_
これにより、ファイル内の「テキスト」で始まるすべての行が「置換」で始まるように置き換えられます。
テスト:
_^/.[a]|$0\\{7}!!^/.[a]|$0\\{7}!!^/.[a]|$0\\{7}
_内で、リテラル_^/.[a]|$0\\{7}
_を文字どおり_$0\\
_に置き換えます
_printf '%s' '^/.[a]|$0\\{7}!!^/.[a]|$0\\{7}!!^/.[a]|$0\\{7}' \
| xxd -p | tr -d '\n' \
| Perl -pe "s/$(printf '%s' '^/.[a]|$0\\{7}' | xxd -p | tr -d '\n')(?=(?:.{2})*\$)/$(printf '%s' '$0\\' | xxd -p | tr -d '\n')/g" \
| xxd -p -r
_
出力:
_$0\\!!$0\\!!$0\\
_
PHPの str_replace を使用できます。
php -R 'echo str_replace("\|!£$%&/()=?^\"'\''","replace",$argn),PHP_EOL;'<input.txt >output.txt
注:単一引用符をエスケープする必要があります'
および二重引用符"
、しかし。
ほとんどのシステムにはpythonを使用する準備ができています。このため、yaで機能する簡単なスクリプトを次に示します。
# replace.py
# USAGE: python replace.py bad-Word good-Word target-file.txt
#
import sys
search_term = sys.argv[1]
replace_term = sys.argv[2]
target_file = sys.argv[3]
with open(target_file, 'r') as file:
content = file.read()
content = content.replace(sys.argv[1], sys.argv[2])
with open(target_file, 'w') as file:
file.write(content)
1つの警告:これは、良い/悪い単語がすでにシステム/環境変数にある場合にうまく機能します。スクリプトに渡すときに、変数をラップするために二重引用符を使用していることを確認してください。
例えば:
python replace.py "$BAD_Word" "$GOOD_Word" target-file.txt
ただし、これらは機能しません:
# This breaks on $ or " characters
BAD_Word="your-artibrary-string"
# This breaks on ' characters
BAD_Word='your-artibrary-string'
# This breaks on spaces plus a variety of characters
BAD_Word=your-artibrary-string
スクリプトに任意のリテラル値を提供する必要がある場合(エスケープをスキップして)、通常は次の方法を使用してディスクに書き込みます。
head -c -1 << 'CRAZY_LONG_EOF_MARKER' | tee /path/to/file > /dev/null
arbitrary-one-line-string
CRAZY_LONG_EOF_MARKER
... どこ:
head
とtee
を使用していますここにトリッキーな文字を使った簡単なデモがあります:
head -c -1 << 'CRAZY_LONG_EOF_MARKER' | tee /path/to/file > /dev/null
1"2<3>4&5'6$7 # 8
CRAZY_LONG_EOF_MARKER
Wordファイルから読み取る更新されたスクリプトは次のとおりです。
# replace.py
# USAGE: python replace.py bad-Word.txt good-Word.txt target-file.txt
#
import sys
search_term_file = sys.argv[1]
replace_term_file = sys.argv[2]
target_file = sys.argv[3]
print [search_term_file, replace_term_file, target_file]
with open(search_term_file, 'r') as file:
search_term = file.read()
with open(replace_term_file, 'r') as file:
replace_term = file.read()
with open(target_file, 'r') as file:
content = file.read()
print [search_term, replace_term]
content = content.replace(search_term, replace_term)
with open(target_file, 'w') as file:
file.write(content)
アルパインのDockerコンテナーで作業しているので、python/pearl/Ruby/python to検索と置換の非常に単純な操作を実行しますこれらのソリューションはすべて恐ろしく複雑です!!
これには2つの実行可能な解決策があります。
最小のDockerコンテナーで作業しているため、最初のケースは実行できません。
これ ソリューションは2番目に使用できます
私の場合、ファイルに既知の文字列:_{{replace_me}}
_とユーザー入力がありました。それを_$replace_text
_と呼びましょう。
_sed -i "s/{{replace_me}}/$(sed 's/[&/\]/\\&/g' <<<"$replace_text")/g" path/to/file
_
どのように機能しますか?
インプレース変換には_sed -i
_を使用します。ここでは、_\
_を区切り文字として使用しています。これは、置換行で具体的にエスケープしているためです。これは、ユーザーが_my\string
_を置くのを防ぎます。
$(sed 's/[&/\]/\\&/g' <<<"$replace_text")
ビットは詳細に説明されています here このソリューションが導き出された素晴らしい答えです。この場合、私はそれをワンライナーとして使用しています
OPの最初の質問への回答として、トリックを実行するsed one linerを次に示します。
_sed -i "s/$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$search_text")/$(sed 's/[&/\]/\\&/g' <<<"$replace_text")/g" path/to/file
_
しかし、それは7年になったことを考えると、おそらくもう気にしないと思います!
これは ハッシュブラウンの回答 (および ウェフの回答 の非常に類似した質問への拡張)の拡張です。
さまざまな特殊文字と文字列の特別な意味の問題を削除できます(_^
_、_.
_、_[
_、_*
_、_$
_、_\(
_、_\)
_、_\{
_、_\}
_、_\+
_、_\?
_、_&
_、_\1
_、…、何でも、および_/
_ delimiter)byremoving the special characters。具体的には、すべてを16進数に変換できます。その後、_0
_-_9
_およびa
-f
のみを処理します。この例は、原理を示しています。
_$ echo -n '3.14' | xxd
0000000: 332e 3134 3.14
$ echo -n 'pi' | xxd
0000000: 7069 pi
$ echo '3.14 is a transcendental number. 3614 is an integer.' | xxd
0000000: 332e 3134 2069 7320 6120 7472 616e 7363 3.14 is a transc
0000010: 656e 6465 6e74 616c 206e 756d 6265 722e endental number.
0000020: 2020 3336 3134 2069 7320 616e 2069 6e74 3614 is an int
0000030: 6567 6572 2e0a eger..
$ echo "3.14 is a transcendental number. 3614 is an integer." | xxd -p \
| sed 's/332e3134/7069/g' | xxd -p -r
pi is a transcendental number. 3614 is an integer.
_
もちろん、_sed 's/3.14/pi/g'
_は_3614
_も変更します。
上記は少し単純化しすぎています。境界は考慮されていません。次の(少し工夫された)例を考えてみます。
_$ echo -n 'E' | xxd
0000000: 45 E
$ echo -n 'g' | xxd
0000000: 67 g
$ echo '$Q Eak!' | xxd
0000000: 2451 2045 616b 210a $Q Eak!.
$ echo '$Q Eak!' | xxd -p | sed 's/45/67/g' | xxd -p -r
&q gak!
_
_$
_(_24
_)とQ
(_51
_)は結合して_2451
_を形成するため、_s/45/67/g
_コマンドは内部から切り離します。 _2451
_を_2671
_に変更します。これは_&q
_(_26
_ + _71
_)です。検索テキスト、置換テキスト、およびファイルでスペースを使用してデータのバイトを分離することにより、これを防ぐことができます。これが定型化されたソリューションです。
_encode() {
xxd -p -- "$@" | sed 's/../& /g' | tr -d '\n'
}
decode() {
xxd -p -r -- "$@"
}
left=$( printf '%s' "$search" | encode)
right=$(printf '%s' "$replacement" | encode)
encode path/to/the/file | sed "s/$left/$right/g" | decode
_
encode
関数を定義したのは、その機能を3回使用してから、対称性のためにdecode
を定義したからです。 decode
関数を定義したくない場合は、最後の行を
_encode path/to/the/file | sed "s/$left/$right/g" | xxd -p –r
_
encode
関数は、ファイル内のデータ(テキスト)のサイズを3倍にし、最後に改行を入れずに、sed
を1行として送信します。 GNU sedはこれを処理できるようです。他のバージョンでは処理できない可能性があります。また、これによってファイルがインプレースで変更されることはありません。出力を一時ファイルに書き込む必要があります元のファイルにコピーします(またはそれを行うための他のトリックの1つ)。
追加のボーナスとして、このソリューションは複数行の検索と置換(つまり、改行を含む検索文字列と置換文字列)を処理します。
もう1つの「ほぼ」機能する方法。
Viまたはvimを使用します。
置換したテキストファイルを作成します。
:%sno/my検索文字列\\ "-:#2; g( '。j'); \\">/my replacestring = \\ "bac)(o:#46; \\" >/ :x
次に、コマンドラインからviまたはvimを実行します。
vi -S commandfile.txt path/to/the/file
:%snoは、魔法を使わずに検索と置換を行うviコマンドです。
/は、選択したセパレーターです。
:xはviを保存して終了します。
バックスラッシュ「\」をエスケープする必要があります。フォワードスラッシュ「/」は次のように置き換えることができます。疑問符「?」または検索または置換文字列にない何か他のもの、パイプ '|'私のために機能しませんでした。
ref: https://stackoverflow.com/questions/6254820/perform-a-non-regex-search-replace-in-vimhttps://vim.fandom.com/ wiki/Search_without_need_to_escape_slashhttp://linuxcommand.org/lc3_man_pages/vim1.html
@Nowakerに相当するNode.JS:
export FNAME='moo.txt'
export FIND='search'
export REPLACE='rpl'
node -e 'fs=require("fs");fs.readFile(process.env.FNAME,"utf8",(err,data)=>{if(err!=null)throw err;fs.writeFile(process.env.FNAME,data.replace(process.env.FIND,process.env.REPLACE),"utf8",e=>{if(e!=null)throw e;});});'