[英]Looping through files in a dir; Pulling out filenames to replace string(s) in existing files
我有一個markdown文件目錄,我正在嘗試通過以下方法完成此操作:
我已經關閉了,但是以下代碼僅提取第一個markdown文件的文件名,並將變量應用於文件中的所有字符串。 到目前為止,這是我的工作代碼:
#!/bin/bash
for file in /home/user/dir/*; do
str="somestring"
filename=$(basename $file)
fn="$(echo "${filename%.*}")"
find ./ -type f -exec sed -i '' -e "s/${str}/${fn}/g" {} \;
done
假設markdown文件如下所示:
123456789.md
,位於/home/user/dir/123456789.md
,帶有其他幾個.md文件和其他隨機數字名稱。
.md文件的結構類似於:
---
layout: default
date: 2010-03-28
original: /orig/somestring.jpg
thumbnail: /thumb/somestring_thumb.jpg
permalink: /images/somestring/
---
我的目標是使腳本根據.md文件本身的文件名使每個文件看起來像這樣:
---
layout: default
date: 2010-03-28
original: /orig/123456789.jpg
thumbnail: /thumb/123456789_thumb.jpg
permalink: /images/123456789/
---
對編輯sed調用的最佳方法或編寫此方法的其他方法有何想法? 有時在我的測試中,sed返回sed: RE error: illegal byte sequence
,但是無論如何都是使用字符串的重命名,即使它是錯誤的字符串。
考慮使用以下非常健壯的解決方案。 它可以確保在給定的搜索字符串和/或Markdown文件名中可能被解釋為基本正則表達式 (BRE)元字符的任何字符都被視為sed
替換中的文字。
#!/usr/bin/env bash
target_dir=/path/to/dir
search='somestring'
search_escaped=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$search")
while read -rd ''; do
base=$(basename -- "$REPLY")
replace_escaped=$(sed 's/[&/\]/\\&/g' <<<"${base%.*}")
sed -i '' -e 's/'"$search_escaped"'/'"$replace_escaped/g"'' "$REPLY"
done < <(find $target_dir -depth 1 -type f -name '*.md' -print0)
說明:
target_dir
變量的值應定義為要在其中進行搜索的目錄的路徑名。例如,在問題中指定的/home/user/dir
。
應將search
變量的值更改為要在markdown( .md
)文件中搜索的字符串,並且必須將其括在單引號( '...'
)中。
讀取的行;
search_escaped=$(sed 's/[^^]/[&]/g; s/\\^/\\\\^/g' <<<"$search")
轉義search
字符串中可能存在的潛在BRE元字符,並將結果分配給名為search_escaped
的新變量。
我們這樣做是因為最終您定義的搜索字符串將用作sed的s
命令的搜索字符串,即s/regexp/replacement/flags
。 基本上你給的每一個字符search
字符串被放置在其自己的字符集[...]
的表達把它當作文字,除了插入符號( ^
)字符(S),因為他們得到轉義為\\^
。 有關更多詳細信息,請參考此答案 。
這意味着我們可以提供一個search
字符串,例如s$om *e[s\\t^ring
,即具有很多元字符的search
字符串,它們將被視為文字,從而防止程序出錯。
使用find
實用程序,我們定義以下命令來獲取給定target_dir
中所有.md
文件的路徑名:
find $target_dir -depth 1 -type f -name '*.md' -print0
while
循環讀取 find
命令的結果,即find
的每個.md
文件的路徑名。
在while
循環的主體中,我們執行以下任務:
我們從每個路徑名中獲取基本名(注意: $REPLY
是與while
相關的內置變量-在這種情況下,它在循環的每個回合中都持有對路徑名的引用):
base=$(basename -- "$REPLY")
該行顯示為:
replace_escaped=$(sed 's/[&/\\]/\\\\&/g' <<<"${base%.*}")
轉義sed
可以感知的占位符,例如文件名\\1
。 例如; 如果文件名為somefile\\1\\2\\3.md
,當我們用它替換search
字符串時該文件將失敗-但是,這樣做可以防止這種情況。 同樣,請參閱此答案以獲取更多詳細信息。
${base%.*}
部分利用參數擴展來從base
變量的值(即,從文件名/基名)中省略文件擴展名部分(即.md
)。
最后,我們將Markdown文件中可能存在的搜索字符串的所有實例(即$search_escaped
變量的值)替換為replace_escaped
變量的值(即不帶文件擴展名的文件名)。
sed -i '' -e 's/'"$search_escaped"'/'"$replace_escaped/g"'' "$REPLY"
已知問題:基本名稱的任何部分都可能包含換行符( \\n
),盡管此解決方案可以使用此處描述的方法正確處理此類路徑名的發現-當前文件名包含以下內容時,它不執行字符串替換換行符。
如果我正確理解,則可以進行以下操作:
#!/bin/bash
for file in /home/user/dir/*; do
str="somestring"
filename=$(basename "$file")
fn=${filename%.*}
LANG=C sed -i '' -e "s/${str}/${fn}/g" "$file"
done
問題是您正在for
循環中執行find & sed
,這會替換不相關文件中的字符串。
sed
之前的LANG=C
是sed
的常見解決方法sed: RE error: illegal byte sequence
問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.