[英]Replace string in the first file it is found
我有一堆這樣命名的文件:
chapter1.tex
chapter2.tex
chapter3.tex
...
chapter 10.tex
chapter 11.tex
etc.
我正在嘗試使用sed
在所有文件中查找並用ZZZZZZ
替換AAAAAA
的第一個實例。
sed -i "0,/AAAAAA/s//ZZZZZZ/" chapter*.tex
我試過這個上面的命令,但有兩個問題:
AAAAAA
的第一個實例。 我只想要所有文件中的第一個實例。ls
那么chapter10.tex
會在chapter1.tex
之前列出。 按章節順序搜索文件至關重要。 如何使用 Bash 工具從一大堆文件中查找和替換第一個實例,所以只替換第一個找到的文件中的第一個實例,同時也尊重文件順序( chapter1.tex
是第一個, chapter10.tex
是第十)?
這是一個基於 bash 循環的解決方案,它可以處理諸如chapter 10.tex
文件名,即帶空格的文件名等:
while IFS= read -r -d '' file; do
if grep -q 'AAAAAA' "$file"; then
echo "changing $file"
sed -i '0,/AAAAAA/s//ZZZZZZ/' "$file"
break
fi
done < <(printf '%s\0' chapter*.tex | sort -z -V)
這是假設sed
和sort
都來自 gnu utils。
如果你有支持就地編輯的 gnu awk 4+ 版本,即-i inplace
那么你可以用單個awk
替換grep + sed
:
while IFS= read -r -d '' file; do
awk -i inplace '!n {n=sub(/AAAAAA/, "ZZZZZZ")} 1;
END {exit !n}' "$file" && break
done < <(printf '%s\0' chapter*.tex | sort -z -V)
使用完整的 GNU 工具箱,您不需要循環。
printf '%s\0' chapter*.tex \
| sort -zV \
| xargs -0 grep -FlZ 'AAAAAA' \
| head -zn1 \
| xargs -0r sed -i 's/AAAAAA/ZZZZZZ/'
這可能對您有用(GNU sed 和 grep):
grep -ns 'AAAAAA' chapter{1..9999}.txt | head -1 |
sed -nE 's#([^:]*):([^:]*):.*#sed -i "\2s/AAAAAA/ZZZZZZ/" \1#e'
使用 grep 和 bash 的大括號擴展來識別一個可能匹配的文件和行號,並構建一個 sed 腳本以在該行號處更新該文件。
NB 大括號擴展以正確的順序生成文件名,grep 的-s
命令行選項會抑制不存在的文件消息。
使用 GNU 並行的替代方法:
grep -sno 'AAAAAA' chapter{1..9999}.txt | head -1 |
parallel --colsep : sed '{2}s/{3}/ZZZZZZ/' {1}
我站在巨人的背上,哈哈
感謝@potong 提供了帶有支架擴展的出色排序解決方案! 這意味着整個事情可以簡化為單進程單行:
sed -i '0,/^AAA/{ /^AAA/{ s/AAA/ZZZ/; h; } }; ${ x; /./{x;q;}; x; }' chapter\ {[0-9],[0-9][0-9]}.tex
正如所指出的,下面的原始解決方案將處理和更改每個文件中的第一次出現,並且不會更正文件順序。 @anubhava 已經提供了一個優秀、優雅的排序解決方案,我不會嘗試改進。
while IFS= read -r -d '' file; do lst+=( "$file" ); done < <(printf '%s\0' chapter*.tex | sort -z -V)
這會以正確的順序創建一個文件名列表,可以將其傳遞給sed
的單個調用以集體處理它們。
要將其應用於基於sed
的解決方案的排序,並且僅在任何文件中出現第一次出現 -
sed -i '0,/^AAA/{ /^AAA/{ s/AAA/ZZZ/; h; } }; ${ x; /./{x;q;}; x; }' "${lst[@]}"
這將期待通過每個文件並改變它發現該文件中的第一次出現, h
掩門在第一次發現它的線。 在每個文件的最后一行用電子郵件x
更改為保持緩沖區,檢查是否掉之后存在模式緩沖區任何當前行。 如果沒有,它會將其交換回來並繼續。 如果有,它交換回來和q
UITS,跳過所有后續文件。
雖然有些復雜,但這不會為每個文件生成進程。
使用雙重條件 -
sed -i '0,/AAAAAA/{ /AAAAAA/s/AAAAAA/ZZZZZZ/ }' chapter*.tex
要查看相同的一般邏輯,請執行以下操作:
$: cat a.tex b.tex
111
AAA
BBB
AAA
222
111
AAA
BBB
AAA
222
$: sed -i '0,/^AAA/{ /^AAA/s/AAA/ZZZ/; }' *.tex
$: cat a.tex b.tex
111
ZZZ
BBB
AAA
222
111
ZZZ
BBB
AAA
222
'0,/^AAA/
是正確的,因為它的范圍從文件的開頭到目標字符串的第一次出現。
{
打開一個塊,我們可以在其中使用第二次搜索來確保它只影響目標字符串。
在區塊內部, /^AAA/s/AAA/ZZZ/;
替換 AAA 字符串並忽略它之前的所有記錄。 }
關閉塊。 之后的所有記錄都將保持不變。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.