【问题标题】:Bash script search for file and replace if string found inside file using wildcardsBash脚本搜索文件并替换是否使用通配符在文件内找到字符串
【发布时间】:2012-09-06 22:54:37
【问题描述】:

我正在开发一个 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 脚本,我可以将通配符路径或文件名传递给它,它会搜索该文件,一旦找到它搜索特定术语的文件并在找到时替换它。

听起来很简单,应该是这样,但我把头撞在桌子上,因为我已经很久没有搞砸 bash...aaggghh 帮助!

【问题讨论】:

  • 顺便说一句,定义$PATH 会覆盖由shell 定义的PATH 变量,该变量告诉它在运行命令时要查看哪些文件夹。例如,当您键入python myscript.py 时,shell 会在$PATH 中以分号分隔的目录搜索名为python 的可执行文件并使用它——它消除了键入/usr/bin/python myscript.py 的需要。通过覆盖它,您可能会破坏脚本中遵循您的定义的部分。 TLDR:您真的应该考虑为变量使用不同的名称。
  • 作为后续,这就是为什么您必须写 /usr/bin/clear 而不仅仅是 clear 以及 William Pursell 答案背后的原因。

标签: linux bash shell unix


【解决方案1】:

首先,将变量名以大写形式声明是一个糟糕、糟糕、非常糟糕的主意,尤其是PATH,因为您实际上会重新设置实际的PATH 环境变量(只需执行@987654323 @ 在提示中你会明白我的意思,这可能是你必须使用/usr/bin/clear 而不是clear 的原因之一,因为环境变量PATH 被修改了)。因此,始终建议使用小写变量,如path


您面临的问题是子shell 中*glob 扩展。当你使用

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

* 不会在提示符(父 shell)处展开,而是在脚本的子 shell 内展开。但是当你使用

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

* 在父 shell 中展开(这将导致 shell 指向一个随机路径),然后在程序子 shell 中传递-

假设/home 有目录/user/apps/data 执行$ cd * 会将您带到按字母顺序排列的目录,即/apps。所以 /home/* 在父 shell 中不带引号将始终指向 /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),shell 会将 *? 等字符视为普通字符,并且不会扩展它们这可能会导致不良结果和混乱。

【讨论】:

  • +1 设置 PATH 的好调用 + 必须调用 /usr/bin/... 来处理以下所有内容
【解决方案2】:
find $path -name \*$filenamepattern\* -exec sed -i.bak 's/find/replace/g' {} ';'
  • sed -i 会在文件中查找和替换保存回文件
  • 如果您不想备份文件,请删除 .bak 部分
  • 利用 find 的 -exec 选项来避免稍微麻烦的 xargs

【讨论】:

  • 我不确定这个答案与问题有什么关系。 OP 希望在路径周围不带引号的情况下运行 ./script '/home/*/public_html' php.ini module.so,并且想知道为什么该命令在没有引号的情况下不起作用。
【解决方案3】:

只需重新排序您的参数并将您要搜索的目录列表放在最后。所以:

#!/bin/sh

FILENAME="${1?No filename specified}"
SEARCHFOR="${2?No target string given}"
shift; shift;
SEARCHPATH="$@"

那么您应该能够保持其余部分不变(除了将名称 PATH 更改为 SEARCHPATH,因为更改 PATH 是一个非常糟糕的主意)并将其称为:

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

【讨论】:

  • +1 不仅是因为投反对票(对选民感到羞耻;您应该发表评论指出哪里出了问题)。这是正确的解决方案,符合广受推崇的 Unix 约定,只是因为它简单且不令人惊讶。
  • 但是* 在传递给子shell(即程序)之前不会被扩展吗?目的是让* 在程序子shell 内展开,但如果* 没有被引号包围,它会在父shell 本身中展开! (仅供参考-虽然我不是反对者)@william pursell
  • @Annjawn 是的,* 将在传递给脚本之前进行扩展。但是"$@" 是扩展值,然后传递给find
  • 哦,我现在明白了...您使用的是 $@。
【解决方案4】:
find `filepath` -name `filename`|xargs sed 's/find/replace/'

对我来说,我会使用 find 和 sed 来完成工作。

【讨论】:

  • 反引号?为什么要在filepathfilename 周围使用反引号。此外,其中没有 $,它们看起来像变量(它们绝对不是 shell 命令)。
【解决方案5】:

这将解决将通配符传递给 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 {} ';'
  • 在命令行上调用此脚本时很重要,您必须反斜杠转义您的 glob;
    • % ./thisscript . \*.csv oldword newword
  • bash -f 选项在正确的时间关闭 globbing - 首次调用 bash 子shell 时,即在解析参数之前;
  • 在所有匹配的文件上查找命令 execs sed
  • 将搜索替换模式传递给包含脚本中其他已定义变量的 sed 的最简单方法是首先将模式本身放入变量中
  • -i.bak 选项创建一个带有替换的新文件,并备份原始文件

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-19
    • 2020-05-12
    • 2018-05-11
    • 2021-07-08
    • 2012-04-21
    • 2019-11-03
    • 1970-01-01
    相关资源
    最近更新 更多