Bash 避免解释特殊字符

Bash 避免解释特殊字符

大家好。我用 bash 编写的脚本有问题。该脚本负责在 [PATH] 中简单地递归搜索 [INPUT FILE] 中给出的模式。如果未找到模式,则将其写入 [OPTIONAL OUTPUT FILE]。如果没有给出 [OPTIONAL OUTPUT FILE],则默认的 [OUTPUT FILE] 名称为:out。问题出在特殊字符“.”(点)下面是脚本的代码:

#!/bin/bash

#This script is responsible for simply searching recursively patterns given in input file in path where we have to search. If pattern is not found then is written to output file;
#@version 1.0

function help()
{
    echo -e "This script is responsible for simply recursively searching patterns\ngiven in [INPUT FILE] in [PATH]. If pattern is not found then it is\nwritten to [OPTIONAL OUTPUT FILE]. If [OPTIONAL OUTPUT FILE] is not\ngiven the default [OUTPUT FILE] name is: out"
    echo 'Usage: ./search.sh [INPUT FILE] [PATH TO DIRECTORY] [OPTIONAL OUTPUT FILE]'
    echo 'e.g. : ./search.sh input_file /var/www/html/ output_file'
    echo 'or   : ./search.sh help -> this help'
}

in=$1
path=$2
out=${3:-out}

if [ $# -lt 2 ]; then help; exit; fi

if [ ! -e $in ]; then echo "Input file: $1 does not exist"; exit; fi

if [ ! -d $path ]; then  echo "Path: $path does not exist"; exit; fi

#Delete lines that are either blank or only contain spaces
sed -i '/^ *$/d' $in

tmp='tmpFile'
cat $in | sed -e 's,\\,\\\\,g' | sed -e 's,\",\\\",g' |  sed -e 's,-,\\-,g' | sed -e 's/\./\\./g' > $tmp
counter=0
#Write each line from input file and save it to array
while read line
do
    linesTable[$counter]=$line
    let counter++
done < $tmp

#Clear file
echo -n '' > $tmp

for line in "${linesTable[@]}"
do
    #Find recursively pattern line in path and save result to array
    echo "$line"
    table=($(grep -r -- "$line" $path))
#   echo $(grep -r -- "$line" $path)
    #If array is empty write string to tmp file
    if [ 0 -eq ${#table[@]} ]; then echo "$line" | tee -a $tmp; fi
done

#Free memory taken by arrays
unset table[@]
unset linesTable[@]
#Sort and remove repeated strings. Result save to output file
sort $tmp | uniq > $out
#Remove tmp file
rm -f $tmp

我无法避免 shell 解释“。”这里是调用的输入文件的内容:

asdf
1234
ALA MA
gtrrr
@
% asdf
~i
?
+
{
|
`
(
)
.
*
-
'
"
""
--
,
;
:
~
\\
\
~~~
printg("asdf\d%d\\\", &g);

包含文件的路径例如为 /home/user/test/ 在此路径中我有 3 个文件例如 abc: a)

dddno
asdf

asdfasd

asdf
asd

b)

s;dfhiasdf
asdf
asd
fas--
--
0

asdf-
-

C)

d
dafdf
dd
re v
1234
v
c
v

我运行这样的脚本:./search.sh in /home/user/test/ out 。在输出文件中:out 应该是 . (点),但没有。smb 能帮我解决这个问题吗?我一直卡在这个地方。提前谢谢您。


你好,Dennis。谢谢你的建议。它确实对我有帮助,但我还有一些问题:此脚本的目的是在给定路径的 input_file 中查找字符串模式。所以我想我必须使用 grep -F 选项并删除部分 sed 表达式。

sed -i '/^ *$/d' "$in"

但我不知道如何删除全局空白行和空格,以便看起来像您所做的那样。我试过这个,但没有用:

<"$in" sed -e '/^ *$/d'

所以我得到了我的解决方案。第二个问题是你的代码部分(附加到数组)对我来说不起作用:

patterns+=("$line")

我收到此错误:

./search.sh: line 45: syntax error near unexpected token `"$line"'
./search.sh: line 45: `         patterns+=("$line")'

我尝试过使用 let 但是它也不起作用。

The script now looks like: 
#!/bin/bash

in="$1"
path="$2"
out=${3:-out}

function help()
{
    cat << EOF
This script is responsible for simply recursively searching patterns given in [INPUT FILE] in [PATH]. If pattern is not found then it is written to [OPTIONAL OUTPUT FILE]. If [OPTIONAL OUTPUT FILE] is not given the default [OUTPUT FILE] name is: out
Usage: $0 [INPUT FILE] [PATH TO DIRECTORY] [OPTIONAL OUTPUT FILE]
e.g. : $0 input_file /var/www/html/ output_file
or   : $0 help -> this help
EOF
}

#Delete lines that are either blank or only contain spaces
function extract_patterns()
{
    sed -i '/^ *$/d' "$in"
}

function report_missing_patterns()
{
    local pattern

    for pattern in "$@"; do
        grep -q -r -F -- "$pattern" "$path"
        #if [ 0 -ne $? ]; then printf "%s\n" "$pattern"; fi
        if [ 0 -ne $? ]; then echo "$pattern"; fi
    done
}

function process_patterns()
{
    local patterns line counter=0
    patterns=()

    while read -r line; do
        patterns[$counter]="$line"
        let counter++
    done < "$in"

    #report_missing_patterns "${patterns[@]}" | sort -u > "$out"
    report_missing_patterns "${patterns[@]}" | sort -u | tee "$out"
}

if [ $# -lt 2 ]; then help; exit 1; fi

if [ ! -e "$in" ]; then echo "Input file: $in does not exist"; exit 2; fi

if [ ! -d "$path" ]; then  echo "Path: $path does not exist"; exit 3; fi

extract_patterns | process_patterns

我有评论线#report_missing_patterns "${patterns[@]}" | sort -u > "$out"

因为我想在屏幕上显示结果并将其重定向到output_file。

答案1

我不明白你具体遇到了什么问题。你的描述确实不清楚。所以我会给出一些简化脚本的一般建议;如果这还不足以解决你的问题,请尝试给出更清晰的解释。

我确信这个脚本比它需要的要复杂得多。花几分钟浏览命令的文档,看看它的某个选项是否能帮到你,可以节省几个小时的调试时间。花几分钟思考一下脚本的总体结构,可以节省几个小时的调试时间。


这里有几种可以使您的脚本更简单的方法。

  • 全部变量替换应该放在双引号内即总是写"$foo"而不仅仅是$foo。你有时会这样做,但不是系统性的。除非你知道为什么,否则一定要使用双引号不要在特定情况下需要它们。

  • 这是编写help函数的一种更简单的方法;它被称为“此处文档”。

    function help()
    {
        cat <<EOF
    This script is responsible for simply recursively searching patterns
    given in [INPUT FILE] in [PATH]. If pattern is not found then it is
    written to [OPTIONAL OUTPUT FILE]. If [OPTIONAL OUTPUT FILE] is not
    given the default [OUTPUT FILE] name is: out
    Usage: $0 [INPUT FILE] [PATH TO DIRECTORY] [OPTIONAL OUTPUT FILE]
    e.g. : $0 input_file /var/www/html/ output_file
    or   : $0 help -> this help
    EOF
    }
    
  • 给你的脚本非零退出代码表示失败

    if [ $# -lt 2 ]; then help; exit 2; fi
    if [ ! -e "$in" ]; then echo "Input file: $1 does not exist"; exit 2; fi
    if [ ! -d "$path" ]; then  echo "Path: $path does not exist"; exit 2; fi
    
  • 修改输入文件in令人惊讶,您可以将仅空白的行删除与sed在某些字符前添加反斜杠的多个表达式结合起来。

    <"$in" sed -e '/^ *$/d' -e 's,[-\\".],\\&,g' > "$tmp"
    

    但是你在这里执行的引用很奇怪。为什么你引用了-",它们对 来说并不特殊grep,但对 和 却没有引用,*而 和[是特殊的?这些模式的语法应该是什么?

    如果您想要查找的模式是文字字符串,那么所有这些工作都是不必要的(除了删除仅包含空格的行):调用grep -F

  • 在从 读取行的部分中$tmp,您不需要变量counter,只需将其附加到数组即可。您还需要将参数传递-rread内置函数,这样它就不会删除一些反斜杠。

    while read -r line; do
        linesTable+=("$line")
    done <"$tmp"
    
  • 在模式循环中,您将 的输出存储grep在一个变量中,但您所做的只是测试是否grep找到匹配项。使用 的返回代码会更容易(也更快)grep。(我还从循环中删除了可能是调试输出的内容;您不需要tee附加到文件,只需使用重定向运算符>>。)

    for line in "${linesTable[@]}"; do
        grep -q -r -- "$line" "$path"
        if [ $? -ne 0 ]; then echo "$line" >>"$tmp"; fi
    done
    
  • 您不需要在脚本结束时释放内存。如果这确实是更大脚本的一部分,则应使用内置函数声明它们local


这是命令行解析后脚本部分的重构版本。我整合了上面概述的局部更改,并使用函数使结构更清晰。请注意,更清晰的结构意味着我不需要使用临时文件。我不知道生成的脚本是否符合您的要求,因为您没有准确解释您想要什么。

function extract_patterns () {
    <"$in" sed -e '/^ *$/d' -e 's,[-\\".],\\&,g'
}

function report_missing_patterns () {
  local pattern
  for pattern in "$@"; do
    grep -q -r -- "$pattern" "$path"
    if [ $? -ne 0 ]; then printf "%s\n" "$pattern"; fi
  done
}

process_patterns () {
  local patterns line
  patterns=()
  while read -r line; do
      patterns+=("$line")
  done
  report_missing_patterns "${patterns[@]}" | sort -u >"$out"
}

extract_patterns | process_patterns

答案2

主要内容:

  • 消除sed一行中转义的字符
  • 然后你需要用输入文件的位置替换变量$tmp$in你不需要清除它),但保留它作为输出文件
  • 与 read 一起使用-r以保留反斜杠:while read -r line
  • 使用-F(固定字符串)来grep防止正则表达式解释:table=($(grep -F -r -- "$line" "$path"))

补充笔记:

  • 其实没有必要使用数组linesTable,只要在阅读这些行时进行处理即可
  • 你可以使用sort -u和消除uniq
  • 没有必要取消设置变量,脚本退出时,shell 会为你执行此操作
  • 您实际上不需要tee在循环内反复调用外部。您可以使用另一个echoecho "$line" >> "$tmp"或者将tee循环放在循环外部,因为您已经有一个echo内部:(done | tee "$tmp"那么您不需要-a
  • 您可以tee通过将循环的输出直接放入`sort -u > "$out" 中来消除临时文件的最后使用
  • 所有包含文件名的变量都应始终用引号引起来
  • table不需要是一个数组,因为你不需要访问单个元素

相关内容