在bash和awk中查找print0选项重新实现(perl)

时间:2013-12-10 19:37:58

标签: bash printing null find character

我已成功编写以下功能:

function print0(){
  stdin=$(cat);
  echo "$stdin" | awk 'BEGIN {ORS="\000";}; { print $0}';
}

-print0命令中用作find参数,但基本上用于将其输出传递给此函数的任何命令。它对xargs -0很有用。然后我意识到这个函数的反面也是有用的。我试过以下:

function read0(){
  stdin=$(cat);
  echo "$stdin" | awk 'BEGIN {RS="\000"; ORS="\n";};  {print $0}';

  # EQUIVALENTS:
  # echo "$stdin" | perl -nle '@a=join("\n", split(/\000/, $_)); print "@a"'
  # echo "$stdin" | perl -nle '$\="\n"; @a=split(/\000/, $_); foreach (@a){print $_;}'
}

但它不起作用,有趣的是,当我尝试命令(awk或perl)时,它就像一个魅力:

# WORKING
ls | print0 | awk 'BEGIN {RS="\000"; ORS="\n";};  {print $0}'
ls | print0 | perl -nle '@a=join("\n", split(/\000/, $_)); print "@a"'
ls | print0 | perl -nle '$\="\n"; @a=split(/\000/, $_); foreach (@a){print $_;}'


# DOES NOT WORKING
ls | print0 | read0

我做错了什么?我假设通过以下命令处理空字符有问题:stdin=$(cat);

修改 谢谢大家,结论是bash变量不能保存null值。 PS:提到的命令就是一个例子我知道将空值转换为换行符,反之亦然没有理性的原因。

2 个答案:

答案 0 :(得分:2)

我想说你的实现可以简化为

function print0 { tr '\n' '\0'; }
function read0  { tr '\0' '\n'; }

可以根据需要使用。

但是,它没有增加任何价值;您只需从换行记录切换到NUL分隔的记录,反之亦然,而find ... -print0可以处理多行文件名。你的想法并没有解决这个问题。

您的问题的实际观点 - 如何在bash中处理嵌入NUL字符的字符串 - 已在SO assign string containing null-character (\0) to a variable in bash上进行了讨论。最重要的是,你必须逃脱它们。除此之外,zsh支持嵌入的NUL字符,但显然没有其他shell。

NUL shell内置read字符的处理related discussion on bug-bash,您可能会感兴趣。

答案 1 :(得分:1)

正如其他答案/评论所提到的,您不能在bash字符串变量中放置空字符。但是,如果您可以摆脱变量并只处理管道/流中的数据,那么您可以通过以下方式传递空字符:

function print0() {
  awk 'BEGIN {ORS="\000";}; {print $0}';
}

function read0() {
  awk 'BEGIN {RS="\000"; ORS="\n";};  {print $0}';
}
ubuntu@ubuntu:~/dir$ ls -1
file one
file_two
ubuntu@ubuntu:~/dir$ ls | print0 | read0
file one
file_two
ubuntu@ubuntu:~/dir$ 

以这种方式使用ls也很危险,因为它不适用于包含换行符的文件名。据我所知,find 以编程方式获取目录中文件列表的方式,当文件名中出现奇数字符时。


<强>更新

这是另一种以编程方式获取目录中文件列表的方法,当奇数字符出现在文件名中时,不使用find(或有缺陷的ls)。我们可以使用* glob将目录中所有文件的列表放入bash数组中。然后我们打印出数组的每个成员,使用/ dev / zero的1个字符作为分隔符:

#!/bin/bash

shopt -s nullglob
shopt -s dotglob    # display .files as well

dirarray=( * )

for ((i = 0 ; i < ${#dirarray[@]}; i++)); do
    [ "$i" != "0" ] && head -c1 /dev/zero
    printf "${dirarray[$i]}"
done