19130


**置換前**  
・hoge.jp/stackoverflow

* * *

**置換後**  
・piyo/hoge.jp

* * *

**試したけれどもうまくいかなかったコード**

[code]

    find /path -type f | xargs sed -i 's/hoge.jp%stackoverflow/piyo&hoge.jp/g'
    
[/code]

・意味は良く分かっていません  
・区切りに/が入るとエラーになるとネットに書いてあってので、%で区切っています

* * *

**質問**  
・何がマズいのでしょうか？


CC BY-SA 3.0
2015-11-21T13:16:23.197
19128
2019-12-31T06:45:51.920
7886
question
bash
CentOSで複数ファイル内文字列の一括置換をしたいのですが、うまくいきません…

[code]

    find /path -type f | xargs sed -i 's%hoge\.jp/stackoverflow%piyo/hoge.jp%g'
    
[/code]

とします。

* * *
[code]

    sed -i 's/hoge.jp%stackoverflow/piyo&hoge.jp/g'
    
[/code]

この様に書いてしまいますと、以下の変換を行います。

[code]

    hoge.jp%stackoverflow  =>  piyo&hoge.jp
    
[/code]

sed の `s` コマンドでは、`s` の次の文字がセパレータとして認識されます。このセパレータ文字は `s`
コマンドに指定する正規表現文字列と変換先文字列に含まれない文字であれば使用可能です。ですので、以下は全て同じ変換になります。

[code]

    s%hoge\.jp/stackoverflow%piyo/hoge.jp%g  ## %
    s@hoge\.jp/stackoverflow@piyo/hoge.jp@g  ## @
    s:hoge\.jp/stackoverflow:piyo/hoge.jp:g  ## :
    s_hoge\.jp/stackoverflow_piyo/hoge.jp_g  ## _
    sZhoge\.jp/stackoverflowZpiyo/hoge.jpZg  ## Z
    
[/code]

なお、`.` が任意の一文字にマッチする正規表現なので `\` でエスケープします(`hoge.jp/stackoverflow` 内の `.` のみ)。


CC BY-SA 3.0
2015-11-21T13:35:20.833
19130
2015-11-21T14:08:42.720
2015-11-21T14:08:42.720
19128
answer


`sed` のコマンドライン引数についてはすでに付いてる回答にある通りですが、より完全で、より効率的なコマンドラインをどうぞ。

[code]

    $ find /path -type f -exec sed -i 's%hoge\.jp/stackoverflow%piyo/hoge.jp%g' {} +
    
[/code]

完全→`find` が見つけたパスに空白文字などが含まれていても大丈夫、効率的→`sed` の起動回数を必要最小限に抑えることができる。`xargs`
も不要。

詳細は `find`(1) のオンラインマニュアルをどうぞ。

`find` の `-exec` あるいは `find` と `xargs` の組合せは「意味は良く分かっていません」な状態で利用するのは危険ですよ。


CC BY-SA 3.0
2015-11-21T14:48:08.100
19136
2015-11-21T14:48:08.100
3061
19128
answer


# sed -iの問題

`sed -i`は置換されない場合もファイルのタイムスタンプが更新されてしまいます。  
`ex`コマンドを使って`x`で終了すると置換されていないファイルのタイムスタンプは更新されません。

# 今回紹介するスクリプト(exコマンドを使用)

[code]

    #!/bin/bash
    case $# in
    0|1|2)cat <<EOF 1>&2
    Usage: chp re string files...
    EOF
    exit 1
    esac
    DLMT=$(echo -n -e "\x05")
    re=$1
    str=$2
    shift 2
    
    for file in $@
    do
    ex -s ${file} <<EOF
    :%s${DLMT}${re}${DLMT}${str}${DLMT}g
    x
    EOF
    done
    
[/code]

# 実行例

[code]

    find /path -type f | xargs bash 【今回紹介したスクリプト】△hoge\\.jp△stackoverflow△piyo/hoge.jp
    
[/code]

△は空白です。  
※`.`をエスケープするには`\`を２回指定しなければならないのが難です。


CC BY-SA 4.0
2019-12-31T06:45:51.920
61912
2019-12-31T06:45:51.920
35558
19128
answer