我有一个文件记录了几个目录的结构。我正在尝试单独打印每个目录的文本。我的输入文件如下所示:
$ cat file.txt
/bin:
file_1
file_2
file_3
/sbin:
file_a
file_b
file_c
/usr/local/bin:
doc_a
doc_b
doc_c
我尝试做的是根据用户选择打印文件的特定部分:
#!/bin/bash
PS3=$'\nMake a selection '
select dir in $(grep ':' file.txt;) do
case $REPLY in
[0-9]) echo $dir
# Need something here. Maybe a pcregrep regex?
# pcregrep '(<= $dir)*(some_fancy_regex)' file.txt
break;;
esac
done
向用户显示菜单选项:
1) /bin:
2) /sbin:
3) /usr/local/bin:
Make a selection
假设用户选择2.目前,这只是在屏幕上打印所选目录。我想显示目录及其包含的文件。
/sbin:
file_a
file_b
file_c
从我所读过的内容看来,pcre正则表达式在这里可行。我几乎不了解非pcre风格的正则表达式。我试图将我的大脑包裹在积极和消极的前瞻和前后。看起来很好但我真的不知道我在做什么。如果有人可以帮我解决这个问题,我会很感激。
/
开头,以:
[a-z]
,[A-Z]
,[0-9]
.
_
-
[
答案 0 :(得分:1)
使用GNU sed和bash:
dir="/usr/local/bin:"
sed -n "/${dir//\//\/}/,/^$/{/^$/d;p}" file
使用bash:
dir="/usr/local/bin:"
while IFS= read -r line; do
[[ $line == $dir ]] && switch=1
[[ $line == "" ]] && switch=0
[[ $switch == 1 ]] && echo "$line"
done < file
两种情况下的输出:
/usr/local/bin: doc_a doc_b doc_c
答案 1 :(得分:1)
Grep不是最好的工具,因为它是面向行的;除了使用some contortion之外,你不能真正看到grep查看跨越多行的表达式 - 并且POSIX没有指定-z
选项。
你可以这样做:
#!/bin/bash
PS3=$'\nMake a selection '
mapfile -t opts < <(grep ':' file.txt)
select dir in "${opts[@]}"; do
sed -n "\@$dir@,/^$/{/^$/q;p}" file.txt
break
done
首先,我改变了您的菜单创建。请注意,命令替换中有一个备用分号,后面有一个缺少的分号;如果目录名中有空格,那么使用这样的grep也会破坏。因此,我使用mapfile
将包含:
的所有行放入数组中。
然后,一旦我知道了目录,我就用sed从目录名打印&#34;直到下一个空行&#34;。那只是
sed -n "/$dir/,/^$/p"
但这在多个方面都不尽如人意。首先,目录名称可以包含斜杠,它会使/
分隔的寻址跳闸。我们可以使用\%regexp%
代替/regexp/
,其中%
可以是任何字符;我选择了@
。
现在,我们有
sed -n "\@$dir@,/^$/p"
那几乎就在那里,但打印出空白行;我们使用{/^$/q;p}
而不仅仅是p
来抑制这种情况,而1) /bin blah:
2) /sbin:
3) /usr/local/bin:
Make a selection 1
/bin blah:
file_1
file_2
file_3
表示&#34;如果该行为空,则退出,否则打印它&#34;。
示例输出(编辑为使用带空格的目录名):
{/^$/q;p;}
备注:非GNU seds(就像在macOS中找到的那样)可能会抱怨花括号中的两个命令;使用{{1}}代替(额外的分号)可能会有所帮助。
答案 2 :(得分:1)
它可以完全在bash 4中以单次传递完成,而无需使用任何外部工具。以下是解决此问题的脚本:
#!/bin/bash
# declare an associative array
declare -A dirs=()
# loop thru all lines and populate our associate array
# with dir as key and \n separated file names as value
while read -r; do
[[ -z $REPLY ]] && continue
if [[ $REPLY == *: ]]; then
d="$REPLY"
else
dirs["$d"]+=$'\n'"$REPLY"
fi
done < file.txt
# present a menu to customer and print selected dir name with file names
select dir in "${!dirs[@]}"; do
if [[ -n $dir ]]; then
printf '%s%s\n' "$dir" "${dirs[$dir]}"
break
fi
done
<强>输出:强>
1) /usr/local/bin:
2) /bin:
3) /sbin:
#? 1
/usr/local/bin:
doc_a
doc_b
doc_c
和此:
1) /usr/local/bin:
2) /bin:
3) /sbin:
#? 3
/sbin:
file_a
file_b
file_c
答案 3 :(得分:1)
不要将shell误认为是文本处理工具,这就是awk的用途。您只需要这4行:
$ cat tst.sh
awk -v RS= -F'\n' -v OFS=') ' '{print NR, $1}' file.txt >&2
printf '\nMake a selection: ' >&2
IFS= read -r rsp
awk -v RS= -v nr="$rsp" 'NR==nr' file.txt
$ ./tst.sh
1) /bin:
2) /sbin:
3) /usr/local/bin:
Make a selection: 2
/sbin:
file_a
file_b
file_c