簡體   English   中英

linux命令行中如何替換多個文件中的一個字符串

[英]How to replace a string in multiple files in linux command line

我需要替換一個文件夾中很多文件中的一個字符串,只有ssh訪問服務器。 我怎樣才能做到這一點?

cd /path/to/your/folder
sed -i 's/foo/bar/g' *

出現的“foo”將替換為“bar”。

在 macOS 等 BSD 系統上,您需要根據手冊頁提供一個備份擴展名,如-i '.bak'或“風險損壞或部分內容”。

cd /path/to/your/folder
sed -i '.bak' 's/foo/bar/g' *

類似於 Kaspar 的答案,但使用 g 標志替換一行上的所有事件。

find ./ -type f -exec sed -i 's/old_string/new_string/g' {} \;

對於全局不區分大小寫:

find ./ -type f -exec sed -i 's/old_string/new_string/gI' {} \;

@kev 的回答很好,但只影響直接目錄中的文件。下面的示例使用 grep 遞歸查找文件。 它每次都對我有用。

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @

命令分解

grep -r : --recursive ,遞歸讀取每個目錄下的所有文件。
grep -l : --print-with-matches ,打印每個匹配的文件的名稱,而不是打印匹配的行。
grep -i : --ignore-case

xargs :將 STDIN 轉換為參數,遵循這個答案
xargs -i@ ~command 包含 @~ :在~command~中的特定位置使用的參數的占位符,@ 符號是一個占位符,可以被任何字符串替換。

sed -i :在原地編輯文件,無需備份。
sed s/regexp/replacement/ :用replacement替換匹配regexp的字符串。
sed s/regexp/replacement/ g : global ,對每個匹配項進行替換,而不是僅對第一個匹配項進行替換。

已經列出了一些標准答案。 通常,您可以使用find遞歸列出文件,然后使用sedperl執行操作。

rpl

對於大多數快速使用,您可能會發現命令rpl更容易記住。

在所有.txt文件中將foo替換為bar

rpl -v foo bar '*.txt' 

模擬遞歸地在所有.txt文件中用bar替換正則表達式foo.*

rpl --dry-run 'foo.*' bar '**/*.txt'

您可能需要安裝它( apt-get install rpl或類似的)。

代表

但是,對於涉及正則表達式和反向替換,或文件重命名以及搜索和替換的更艱巨的工作,我所知道的最通用和最強大的工具是repren ,這是我不久前編寫的一個小型 Python 腳本更棘手的重命名和重構任務。 您可能更喜歡它的原因是:

  • 支持文件重命名以及文件內容的搜索和替換。
  • 在您承諾執行搜索和替換之前查看更改。
  • 支持具有反向替換、整個單詞、不區分大小寫和保留大小寫(替換 foo -> bar、Foo -> Bar、FOO -> BAR)模式的正則表達式。
  • 適用於多個替換,包括交換(foo -> bar 和 bar -> foo)或非唯一替換集(foo -> bar,f -> x)。

要使用它, pip install repren 檢查 README以獲取示例。

這對我有用:

find ./ -type f -exec sed -i 's/string1/string2/' {} \;

但是,這沒有: sed -i 's/string1/string2/g' * 也許“foo”不應該是 string1,“bar”不是 string2。

要替換多個文件中的字符串,您可以使用:

grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'

例如

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

源博客

要替換文件中的路徑(避免轉義字符),您可以使用以下命令:

sed -i 's@old_path@new_path@g'

@ 符號表示應忽略后面字符串中的所有特殊字符。

鑒於您要搜索字符串search並將其replace為跨多個文件的替換,這是我經過實戰測試的單行公式

grep -RiIl 'search' | xargs sed -i 's/search/replace/g'

快速grep解釋:

  • -R - 遞歸搜索
  • -i - 不區分大小寫
  • -I - 跳過二進制文件(你想要文本,對吧?)
  • -l - 打印一個簡單的列表作為輸出。 需要其他命令

然后將 grep 輸出通過管道傳輸到 sed(通過 xargs),用於實際替換文本。 -i標志將直接更改文件。 刪除它以獲得一種“試運行”模式。

如果您的字符串中包含正斜杠(/),您可以將分隔符更改為“+”。

find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +

此命令將在當前目錄中遞歸運行。

如果您有可以使用的文件列表

replace "old_string" "new_string" -- file_name1 file_name2 file_name3

如果您擁有所有可以使用的文件

replace "old_string" "new_string" -- *

如果您有帶有擴展名的文件列表,則可以使用

replace "old_string" "new_string" -- *.extension

第一行出現的“foo”將被替換為“bar”。 您可以使用第二行進行檢查。

grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c

“你也可以使用 find 和 sed,但我發現這一小行 perl 工作得很好。

perl -pi -w -e 's/search/replace/g;' *.php
  • -e 表示執行以下代碼行。
  • -i 表示就地編輯
  • -w 寫警告
  • -p 循環

"(摘自http://www.liamdelahunty.com/tips/linux_search_and_replace_multiple_files.php

我最好的結果來自使用 perl 和 grep (以確保文件具有搜索表達式)

perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )

在 MacBook Pro 上,我使用了以下內容(靈感來自https://stackoverflow.com/a/19457213/6169225 ):

sed -i '' -e 's/<STR_TO_REPLACE>/<REPLACEMENT_STR>/g' *

-i ''將確保您不進行備份。

-e用於現代正則表達式。

在找到這個問題(和答案)之前,我確實制定了自己的解決方案。 我搜索了“replace”、“several”和“xml”的不同組合,因為那是我的應用程序,但沒有找到這個特定的應用程序。

我的問題:我有包含復雜對象的測試用例數據的 spring xml 文件。 對 java 源代碼的重構更改了很多類,並且不適用於 xml 數據文件。 為了保存測試用例數據,我需要更改分布在多個目錄中的所有 xml 文件中的所有類名。 同時保存原始 xml 文件的備份副本(盡管這不是必須的,因為版本控制會在這里救我)。

我一直在尋找find + sed的某種組合,因為它在其他情況下對我有用,但不能同時替換多個。

然后我發現ask ubuntu response它幫助我構建了我的命令行:

find -name "*.xml" -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;

而且效果很好(嗯,我的案例有六種不同的替代品)。 但請注意,它會觸及當前目錄下的所有 *.xml 文件。 因此,如果您對版本控制系統負責,您可能希望先過濾並僅傳遞給sed那些實際具有您想要的字符串的人; 喜歡:

find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} \; -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;

真的很蹩腳,但我無法讓任何 sed 命令在 OSX 上正常工作,所以我做了這個愚蠢的事情:

:%s/foo/bar/g
:wn

^- 將這三行復制到我的剪貼板中(是的,包括結尾的換行符),然后:

六 *

並按住 command-v 直到它說沒有文件了。

愚蠢...hacky...有效...

grep --include={*.php,*.html} -rnl './' -e "old" | xargs -i@ sed -i 's/old/new/g' @

multiedit 命令的腳本

multiedit [-n PATTERN] OLDSTRING NEWSTRING

根據 Kaspar 的回答,我制作了一個 bash 腳本來接受命令行參數,並可選擇限制與模式匹配的文件名。 保存在您的 $PATH 中並使其可執行,然后只需使用上面的命令。

這是腳本:

#!/bin/bash
_help="\n
Replace OLDSTRING with NEWSTRING recursively starting from current directory\n
multiedit [-n PATTERN] OLDSTRING NEWSTRING\n

[-n PATTERN] option limits to filenames matching PATTERN\n
Note: backslash escape special characters\n
Note: enclose STRINGS with spaces in double quotes\n
Example to limit the edit to python files:\n
multiedit -n \*.py \"OLD STRING\" NEWSTRING\n"

# ensure correct number of arguments, otherwise display help...
if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
if [ $1 == "-n" ]; then  # if -n option is given:
        # replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
        find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} \;
else
        # replace OLDSTRING with NEWSTRING recursively in all files
        find ./ -type f -exec sed -i "s/$1/$2/" {} \;
fi

我只想添加一個注釋來一次做兩件事 - 找到一個包含字符串的文件,然后使用 find 'chaining' 方法進行替換:

find  . -type f -iname \*.php -exec fgrep -l "www." {} \; -exec sed -i "s|www||g" {} \;      
  • 在這個真實案例中,從 PHP 文件中的 url 中刪除不合時宜的“www”。

  • 'fgrep -l' 只有在文件中找到至少一個匹配項時才會觸發,它不會產生其他輸出。 不要忘記'\;' 分隔符!

當使用-i開關調用時,流編輯器確實會“就地”修改多個文件,該開關以備份文件結尾作為參數。 所以

sed -i.bak 's/foo/bar/g' *

在此文件夾中的所有文件中將foo替換為bar ,但不會進入子文件夾。 但是,這將為您目錄中的每個文件生成一個新的.bak文件。 要對此目錄及其所有子目錄中的所有文件遞歸執行此操作,您需要一個幫助程序(如find )來遍歷目錄樹。

find ./ -print0 | xargs -0 sed -i.bak 's/foo/bar/g' *

find允許您進一步限制要修改的文件,如有必要,通過指定更多參數,如find ./ -name '*.php' -or -name '*.html' -print0


注意:GNU sed不需要文件結尾, sed -i 's/foo/bar/g' *也可以; FreeBSD sed需要擴展,但允許在兩者之間留一個空格,因此sed -i .bak s/foo/bar/g *有效。

為了維護我的個人英文節點,我編寫了一個實用程序腳本,幫助遞歸地替換目錄下的所有文件的多對舊/新字符串。

多對舊/新字符串在哈希映射中進行管理。

目錄可以通過命令行或環境變量設置,地圖在腳本中是硬編碼的,但如果需要,您可以修改代碼以從文件加載。

由於一些新功能,它需要 bash 4.2。

en_standardize.sh:

#! /bin/bash
# (need bash 4.2+,)
# 
# Standardize phonetic symbol of English.
# 
# format:
#   en_standardize.sh [<dir>]
# 
# params:
# * dir
#   target dir, optional,
#   if not specified then use environment variable "$node_dir_en",
#   if both not provided, then will not execute,
# * 
# 

paramCount=$#

# figure target dir,
if [ $paramCount -ge 1 ]; then # dir specified
    echo -e "dir specified (in command):\n\t$1\n"
    targetDir=$1
elif [[ -v node_dir_en ]]; then # environable set,
    echo -e "dir specified (in environment vairable):\n\t$node_dir_en\n"
    targetDir=$node_dir_en
else # environable not set,
    echo "dir not specified, won't execute"
    exit
fi

# check whether dir exists,
if [ -d $targetDir ]; then
    cd $targetDir
else
    echo -e "invalid dir location:\n\t$targetDir\n"
    exit
fi

# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")

# print item maps,
echo 'maps:'
for key in "${!itemMap[@]}"; do
    echo -e "\t$key\t->\t${itemMap[$key]}"
done
echo -e '\n'

# do replace,
for key in "${!itemMap[@]}"; do
    grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
done

echo -e "\nDone."
exit

如果文件包含反斜杠(通常是路徑),您可以嘗試以下操作:

sed -i -- 's,<path1>,<path2>,g' *

前任:

sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all shell scripts available)

像這樣使用 ack 命令會快很多:

ack '25 Essex' -l | xargs sed -i 's/The\ fox \jump/abc 321/g'

此外,如果您在搜索結果中有空格。 你需要逃避它。

使用簡單的腳本文件有一種更簡單的方法:

   # sudo chmod +x /bin/replace_string_files_present_dir

在 gedit 或您選擇的編輯器中打開文件,我在這里使用 gedit。

   # sudo gedit /bin/replace_string_files_present_dir

然后在編輯器中將以下內容粘貼到文件中

   #!/bin/bash
   replace "oldstring" "newstring" -- *
   replace "oldstring1" "newstring2" -- *
   #add as many lines of replace as there are your strings to be replaced for 
   #example here i have two sets of strings to replace which are oldstring and 
   #oldstring1 so I use two replace lines.

保存文件,關閉gedit ,然后退出終端或關閉它然后啟動它以加載您添加的新腳本。

導航到您要編輯多個文件的目錄。 然后運行:

  #replace_string_files_present_dir

按回車鍵,這將自動將包含它們的所有文件中的oldstringoldstring1分別替換為正確的newstringnewstring1

它將跳過所有不包含舊字符串的目錄和文件。

如果您有多個目錄包含需要字符串替換的文件,這可能有助於消除繁瑣的打字工作。 您所要做的就是導航到每個目錄,然后運行:

#replace_string_files_present_dir

您所要做的就是確保您已經包含或添加了所有替換字符串,正如我在上面向您展示的那樣:

replace "oldstring" "newstring" -- *

在文件/bin/replace_string_files_present_dir的末尾。

要添加新的替換字符串,只需在終端中鍵入以下內容打開我們創建的腳本:

sudo gedit /bin/replace_string_files_present_dir

不要擔心您添加的替換字符串的數量,如果找到舊字符串,它們將無效。

以下命令可用於首先搜索文件並替換文件:

find . | xargs grep 'search string' | sed 's/search string/new string/g'

例如

find . | xargs grep abc | sed 's/abc/xyz/g'

我給出了一個修復 python 源代碼中常見 shebang 錯誤的示例。

您可以嘗試 grep/sed 方法。 這是一個適用於 GNU sed 並且不會破壞 git repo 的工具:

$ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \
gsed -i '' -e 's/#!\/usr\/bin\/python/#!\/usr\/bin\/env python/' {}

或者你可以使用greptile :)

$ greptile -x .py -l -i -g '#!/usr/bin/env python' -r '#!/usr/bin/python' .

我剛剛測試了第一個腳本,第二個應該也可以。 小心轉義字符,我認為在大多數情況下使用 greptile 應該更容易。 當然,您可以使用 sed 做很多有趣的事情,為此,最好掌握將它與 xargs 一起使用。

我從另一篇文章中找到了這個(不記得是哪個),雖然不是最優雅的,但它很簡單,作為一個 Linux 新手用戶沒有給我帶來任何麻煩

for i in *old_str* ; do mv -v "$i" "${i/\old_str/new_str}" ; done

如果您有空格或其他特殊字符,請使用 \

for i in *old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done

對於子目錄中的字符串,使用 **

for i in *\*old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done

我對許多答案的問題是我需要替換許多文件中的文件路徑。 盡管提供的一個答案提到了這一點,但它對我不起作用。 我的解決方案:

首先,生成要更改的文件名列表。

filelist=($(find /path/to/your/folder | xargs grep '/path/to/fix' | cut -d : -f 1 | tr '\n' ' '))

上面的命令所做的是通過管道傳輸到grepfind生成文件的名稱,其中包含/path/to/fix 但是, grep也會打印出找到字符串的行,因此cut命令會刪除它並只保留文件名。 tr用空格替換換行符,這允許將filelist存儲為數組。

for file in "${filelist[@]}"; do sed -i.bak 's+/path/to/fix+/new/path/for/my/file+g' $file; done

這個sed命令借鑒了這個問題的其他答案,並使用+作為分隔符而不是普通的/ ,因為/字符正在文件路徑中使用。

我使用了ag ,the_silver_searcher:

ag -0 -l 'old' | xargs -0 sed -ri.bak -e 's/old/new/g';

然后git clean remove.bak 文件( rm在 git rebase exec中運行時出現錯誤)

git clean -f '**/*.bak';

$ replace“ From”“到”-`find / path / to / folder -type f`

(替換-MySQL數據庫系統的字符串替換實用程序)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM