2012-09-06 69 views
1

我正在尋找一個bash腳本,它將使用通配符路徑來搜索特定文件,一旦它發現文件名搜索文件內部然後替換它。bash腳本搜索文件,並替換如果使用通配符找到文件內的字符串

這裏就是我已經把不已:

#!/bin/sh 

PATH=${1} 
FILENAME="$2" 
SEARCHFOR="$3" 

/usr/bin/clear 
echo "Searching $PATH" 
echo "For the file $FILENAME" 
echo "With the string $SEARCHFOR" 
echo "==========================" 
echo "  RESULTS   " 
echo "==========================" 
/usr/bin/find $PATH -type f -name "$FILENAME" | /usr/bin/xargs /bin/grep -l "$SEARCHFOR" 

我還沒有添加更換,但是我想我會用SED替代grep來做到這一點,只是用grep用於測試目的。

有了上面的代碼,我必須使用任何類型的通配符路徑的引號。有沒有解決的辦法?

./script '/home/*/public_html' php.ini module.so 

我在想也許使用參數,但有一種方法可以做到這一點。

我的最終目標是讓bash腳本可以傳遞通配符路徑或文件名,它會搜索該文件,一旦找到文件搜索特定術語並在發現後替換它。

聽起來那麼簡單,它應該是但我敲我的頭靠在桌上,因爲它已經這麼長時間,因爲我已經與慶典搞砸... aaggghh幫助!

+0

附註:定義'$ PATH'會覆蓋由shell定義的'PATH'變量,該變量告訴它要查看哪些文件夾在運行命令時。例如,當你鍵入'python myscript.py'時,shell在'$ PATH'中搜索分號分隔的目錄中的名爲'python'的可執行文件並使用它 - 它不需要鍵入'/ usr/bin/python myscript.py'。通過覆蓋它,您可能會破壞跟隨定義的腳本部分。 TLDR:您應該考慮爲變量使用不同的名稱。 – jedwards

+0

作爲一個後續,這就是爲什麼你必須寫'/ usr/bin/clear'而不是隻是'clear'和William Pursell的答案背後的推理。 – jedwards

回答

2

首先,它是一個壞,壞,真壞主意,有以大寫字母聲明的變量名,尤其是PATH因爲你會實際上是重新設置實際PATH環境變量(只是做一個echo $PATH的提示,你會明白我的意思是,這可能是你不得不使用的原因之一因爲env變量PATH被修改)而不是clear。因此,它總是建議使用像path這樣的小寫變量。


你所面對的問題是與殼層內glob擴張*。當您使用

./script '/home/*/public_html' php.ini module.so 

*沒有在提示符下(父shell)擴展,但腳本的子shell內展開。但是,當你使用

./script /home/*/public_html php.ini module.so 

*在母貝(這將導致一個隨機路徑由外殼指出)擴增,然後裏面的程序subshel​​l-

因此,可以說被傳遞,/home有目錄/user/apps/data$ cd *會帶你到這是第一個字母,即/apps的目錄。所以在父shell中沒有引號的/home/*將始終指向/home/apps,這不是我們想要的!

一個直接的補救措施,這是如下

$set -o noglob #unset glob expansion 
$./script /home/*/public_html php.ini module.so #your command without quotes & wildcard */ 
$set +o noglob #re-set glob expansion back 

$set -o noglob;./script /home/*/public_html php.ini module.so;set +o noglob 

嘗試$echo *,然後$set -o noglob;echo *;set +o noglob爲一個簡單的例子。

買者 - 如果你失敗或忘記設定noglob回(set +o noglob),外殼將把字符,如*?像普通字符,不會擴大它們可能導致不良後果和混亂。

+0

+1好的呼叫設置PATH +必須調用/ usr/bin/...以下所有內容 – maverick

1

只需對您的參數重新排序,並在末尾放置要搜索的目錄列表。所以:

#!/bin/sh 

FILENAME="${1?No filename specified}" 
SEARCHFOR="${2?No target string given}" 
shift; shift; 
SEARCHPATH="[email protected]" 

那麼你應該能夠留下,其餘不變(不是改變名稱路徑來搜索路徑等,因爲改變路徑是一個非常糟糕的主意),並把它作爲:

$ ./script php.ini module.so /home/*/public_html 
+0

+1不僅僅是因爲downvote(選民的羞恥;你應該留下一個評論指出什麼是錯的)。這是正確的解決方案,根據廣泛推崇的Unix慣例,僅僅因爲它簡單而不驚人。 – tripleee

+0

但在傳遞給Subshel​​l(即程序)之前,不會擴展「*」?目的是讓'*'在程序子shell中展開,但如果不包含引號,這個'*'會在父殼體中展開! (僅供參考 - 我不是下面的選民)@威廉姆斯 – Annjawn

+1

@安妮嘉恩是的,'*'在被傳遞給腳本之前會被展開。但''$ @「'是擴展後的值,然後傳遞給'find'。 –

0
find `filepath` -name `filename`|xargs sed 's/find/replace/' 

對我來說我會用find和sed來完成這項工作。

+0

反引號?爲什麼你會在'filepath'和'filename'中使用反引號。另外,這裏沒有'$',它們看起來像變量(並且它們絕對不是shell命令)。 – Annjawn

2
find $path -name \*$filenamepattern\* -exec sed -i.bak 's/find/replace/g' {} ';' 
  • sed的-i會確實發現和文件內更換和回保存到文件
  • ,如果你不想備份文件
  • 槓桿刪除.bak的一部分 - exec選項以避免稍微繁瑣xargs
+0

我不確定這個答案是如何與問題相關的。 OP希望運行'./script'/home/*/public_html'php.ini module.so'而不需要在路徑中引用引號,並且想知道爲什麼命令在沒有引號的情況下無法運行。 – Annjawn

0

這將解決傳送通配符到bash腳本的問題,但請遵守第一個要點。

#!/bin/bash -f 

directory=$1 
filename=$2 
searchfor=$3 
replacement=$4 

sedpattern=s/$searchfor/$replacement/g 

find $directory -name $filename -exec sed -i.bak $sedpattern {} ';' 
  • 重要的,當在命令行中調用這個腳本,你必須反斜槓逃脫你的水珠;
    • % ./thisscript . \*.csv oldword newword
  • 的bash -f選項關閉在正確的時間通配符 - 當首先被調用的bash子外殼,參數被分析之前的含義;
  • find命令高管的sed對符合
  • 最簡單的方法是通過搜索替換模式的sed包含其他定義的變量裏面的腳本是將圖案本身的變量第一
  • -i的所有文件。 bak選項創建一個替換文件,並備份原始文件
相關問題