一个有关 shell 脚本的 sed 和管道的简单问题
需要保存一个 sed 和脚本问题
我想编写一个脚本并执行类似这样的操作 整个任务是编写一个 shell 脚本并将参数转换为 sed 使用的命令,然后调用 sed 从 stdin 打印指定的行。
seq 1 100 | ./script.sh 1 9 34-35
并使用以下命令获取答案:
1 9 34 35
下面是我的代码
#!/bin/bash
str="sed -n -e '"
data=$(cat)
#echo "$data"
for arg in $@;do
if [ `expr index $arg -` -eq 0 ]; then
str="$str${arg}p;"
else
len=`expr length $arg`
i=`expr index $arg -`
i=`expr $i - 1`
tmp1=`expr substr $arg 1 $i`
str="$str${tmp1},"
i=`expr $i + 2`
tmp2=`expr substr $arg $i $len`
str="$str${tmp2}p;"
fi
done
str="$str'"
echo "$str"
`echo $data | $str`
#echo `$str`
~
我收到错误 sed:-e 表达式 #1,字符 1:未知命令:`''
问题是什么 以及 我该如何正确执行此操作?
您的代码构建了一个字符串。
使用参数
1
9
34-35
,您将获得:
sed -n -e '1p;9p;34,35p;'
您调用:
`echo $data | $str`
这变成了两个通过管道连接在一起的命令:
-
echo
使用参数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
-
sed
带参数:-n
-e
'1p;9p;34,35p;'
请注意,shell 不会对单词进行进一步的操作。特别是,引号剥离已经发生(扩展之前没有)。
因此 sed 接收一个以
'
开头的脚本参数。
'
不是有效的 sed 命令,因此会打印错误。
请注意,
$data
应该被引用,否则 shell 将剥离换行符(如上所示),并且运行时
echo
将仅输出一行。
还请注意,如果 sed 成功,反引号将导致 shell 尝试将其输出作为新命令运行。这似乎不太可能是您想要的。
要修复您的代码,通过分离命令和参数来避免混淆会更有意义:
#!/bin/bash
str=
data=$(cat)
#echo "$data"
for arg in $@;do
if [ `expr index $arg -` -eq 0 ]; then
str="$str${arg}p;"
else
len=`expr length $arg`
i=`expr index $arg -`
i=`expr $i - 1`
tmp1=`expr substr $arg 1 $i`
str="$str${tmp1},"
i=`expr $i + 2`
tmp2=`expr substr $arg $i $len`
str="$str${tmp2}p;"
fi
done
echo `echo "$data" | sed -n -e "$str"`
您可以简化/现代化为:
#!/bin/bash
str=
for arg; do
if [ $(expr index $arg -) -eq 0 ]; then
str="$str${arg}p;"
else
len=$(expr length $arg)
i=$(expr index $arg -)
i=$(expr $i - 1)
tmp1=$(expr substr $arg 1 $i)
str="$str${tmp1},"
i=$(expr $i + 2)
tmp2=$(expr substr $arg $i $len)
str="$str${tmp2}p;"
fi
done
# this command will already read from stdin, so no need to buffer the data
echo $(sed -n -e "$str")
expr
使用起来相当繁琐。
利用 bash 的内置功能,您可以进一步简化:
#!/bin/bash
str=
for arg; do
str=$str${arg/-/,}"p;"
done
echo $(sed -n "$str") # output is combined into single line
# for separate lines, just do: sed -n "$str"
或者使用参数验证:
#!/bin/bash
str=
for arg; do
if ! [[ $arg =~ ^[0-9]+(-[0-9]+)?$ ]]; then
echo bad args 1>&2
exit 1
fi
str=$str${arg/-/,}"p;"
done
echo $(sed -n "$str")
这些版本中的任何一个都会产生问题的示例输出。
这可能对您有用(GNU bash 和 sed):
f(){ <<<"$@" sed 'y/-/,/;s/\S\+/&=\n/g'; }
seq 100 | sed -nf <(f 1 9 34-35) | paste -sd' '
创建一个函数
f
,解析所需的参数并生成 sed 脚本以输出输入文件所需的行号。
通过管道传输文件(或提供文件名)并使用 sed 调用,该调用从进程替换中获取 sed 命令并终止隐式打印以将这些行号输出到另一个管道,该管道按顺序粘贴结果并用空格分隔。
该命令可以作为
cat
命令或输入文件调用。
cat file | sed -nf <(f 1 9 34-35) | paste -sd' '
或:
sed -nf <(f 1 9 34-35) file | paste -sd' '
该过程可以变成一行:
sed -nf <(<<<'1 9 34-45' sed 'y/-/,/;s/\S\+/&=;/g') file | paste -sd' '