簡體   English   中英

遍歷目錄中的文件; 拉出文件名以替換現有文件中的字符串

[英]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 
    • -depth 1部分確保我們僅在頂層找到文件。 但是,如果您想遞歸地遞歸給定的目錄樹,則可以將其刪除-通過刪除它,您還將在給定目錄的子目錄中包括多個.md文件,這些文件的層次很深。

    • -name '*.md'部分確保我們僅包括Markdown文件( .md ),並排除給定target_dir可能存在的任何其他文件。

    • 包含在<( ... )find部分,這稱為進程替換 ,而前面的< find的路徑名重定向stdin

  • 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=Csed的常見解決方法sed: RE error: illegal byte sequence問題。

暫無
暫無

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

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