Unix - 需要截取一个以多个空格为分隔符的文件 - 选用 awk 还是 cut?

14

我需要从Unix文本文件中获取记录。分隔符是多个空格。例如:

2U2133   1239  
1290fsdsf   3234
从这个里面,我需要提取。
1239  
3234

所有记录的分隔符始终为3个空格。

我需要在Unix脚本(.scr)中执行此操作,并将输出写入另一个文件或将其用作do-while循环的输入。 我尝试了以下内容:

while read readline  
do  
        read_int=`echo "$readline"`  
        cnt_exc=`grep "$read_int" ${Directory path}/file1.txt| wc -l`  
if [ $cnt_exc -gt 0 ]  
then  
  int_1=0  
else  
  int_2=0  
fi  
done < awk -F'  ' '{ print $2 }' ${Directoty path}/test_file.txt  

test_file.txt是输入文件,file1.txt是查找文件。但上述方式无法正常工作,并给出与awk -F附近的语法错误。

我尝试将输出写入文件。以下命令在命令行中有效:

more test_file.txt | awk -F'   ' '{ print $2 }' > output.txt

这个命令行可以正常运行并将记录写入output.txt文件。但是在Unix脚本(.scr文件)中,同样的命令无法工作。

请告诉我我做错了什么,以及如何解决这个问题。

谢谢,
Visakh


请参见https://dev59.com/GWw05IYBdhLWcg3wmC-_。 - pnkfelix
8个回答

30

将多个分隔符替换为一个的任务由 tr 完成:

cat <file_name> | tr -s ' ' | cut -d ' ' -f 2

tr 翻译或删除字符,非常适合为 cut 正常工作准备数据。

手册 中写道:

-s, --squeeze-repeats
          replace each sequence  of  a  repeated  character  that  is
          listed  in the last specified SET, with a single occurrence
          of that character

1
我最喜欢你的答案,因为使用'tr'和'cut'比'awk'循环更加优雅。谢谢! - valentt
1
man tr `-s`,`--squeeze-repeats` - 用SET1中列出的每个重复字符的输入序列替换为该字符的单个出现。 - Ondra Žižka

12

这取决于你的机器上cut的版本或实现。一些版本支持一个选项,通常是-i,表示“忽略空字段”,或者等效地允许在字段之间使用多个分隔符。如果支持该选项,请使用:

cut -i -d' ' -f 2 data.file

如果不是(并且它并不普遍——也许甚至不常见,因为GNU和MacOS X都没有这个选项),那么使用awk更好、更可移植。
不过你需要将awk的输出导入到循环中:
awk -F' ' '{print $2}' ${Directory_path}/test_file.txt |
while read readline  
do  
    read_int=`echo "$readline"`  
    cnt_exc=`grep "$read_int" ${Directory_path}/file1.txt| wc -l`  
    if [ $cnt_exc -gt 0 ]  
    then int_1=0  
    else int_2=0
    fi  
done

唯一剩下的问题是 while 循环是否在子 shell 中,因此不会修改您的主 shell 脚本变量,而只是修改其自己的变量副本。
对于 bash,您可以使用 进程替换
while read readline  
do  
    read_int=`echo "$readline"`  
    cnt_exc=`grep "$read_int" ${Directory_path}/file1.txt| wc -l`  
    if [ $cnt_exc -gt 0 ]  
    then int_1=0  
    else int_2=0
    fi  
done < <(awk -F' ' '{print $2}' ${Directory_path}/test_file.txt)

这将使得命令输出看起来像来自文件,但离开当前shell的while循环。在${Directory path}中留下的空格通常是不合法的,除非我错过了另一个Bash特性;你也有一个拼写错误(Directoty)。

3
除了其他做同样事情的方法之外,你程序中的错误是:你不能从另一个程序的输出 (<) 进行重定向。将你的脚本改为使用管道,像这样:
awk -F'   ' '{ print $2 }' ${Directory path}/test_file.txt | while read readline

此外,“readline”作为变量名的使用可能会导致问题。

等等,关于“readline”作为变量名的使用,可能会有问题。


2
在这种情况下,您可以使用以下一行代码。
sed 's/   /\t/g' <file_name> | cut -f 2

获取您的第二列。

1

在bash中,您可以从类似以下内容的东西开始:

for n in `${Directoty path}/test_file.txt | cut -d " " -f 4`
{
    grep -c $n ${Directory path}/file*.txt
}

1
这原本应该是一条评论,但由于我暂时无法评论,因此我在这里添加。 这来自一个优秀的答案:https://dev59.com/02855IYBdhLWcg3wy3kb#4483833
tr -s ' ' <text.txt | cut -d ' ' -f4

tr -s '<character>'将多个重复的<character>压缩成一个。


0

Cut 命令不够灵活。我通常使用 Perl 来处理这个问题:

cat file.txt | perl -F'   ' -e 'print $F[1]."\n"'

-F后面,您可以放置任何Perl正则表达式,而不是三个空格。您可以使用$F[n]访问字段,其中n是字段号(从零开始计数)。这样就不需要使用sedtr了。


0

脚本中出现了拼写错误,因为“Directo*t*y path”(您的脚本的最后一行)中有一个错别字,所以它无法正常工作。


这是问题的一部分;名称中的空格也没有帮助。另外,您必须使用更多的符号来获取通过I/O重定向提供为输入的shell命令的输出,而不是使用管道。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接