2012-09-06 43 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选项创建一个替换文件,并备份原始文件
相关问题