一份文件:

a
b

运行命令

dd if=file count=1 skip=0 bs=1 # show a
dd if=file count=1 skip=1 bs=1 # show "newline"
dd if=file count=1 skip=2 bs=1 # show b

我想在bash脚本中使用'if'语句在给定偏移量之前搜索第一个"newline"的偏移量(这是一种伪方法):

para1=$1
while(1)
do
    c=$(dd if=file count=1 bs=1 skip=$para1)
    if [ $c -eq "\n" ]   # How to write this line?
    then
        break
    fi
    para1=`expo $para - 1`
done
echo $para1
bash fun.sh 2
# the output should be 1

实际上,我在这里找到了解决方案:如何比较我的变量在Shell脚本中是否包含换行符

if [ ${#str} -eq 0 ] 

但是我想知道它是否足够健壮,还是有更优雅的方法呢?

分析解答

请专注于代码:

c=$(dd if=test1 skip=2 bs=1 count=1)

man bash的“命令替换”部分描述了:

Bash performs the expansion by executing command ... with any trailing newlines deleted.

因此,删除了上面dd命令结果中的换行符。 您将通过以下测试代码看到它:

for (( i=1; i<=3; i++ )); do
    c="$(dd if=test1 skip="$i" bs=1 count=1 2>/dev/null)"
    echo "skip = $i"
    echo -n "$c" | xxd
done

通常,bash不适合显式处理换行符 字符 因为bash有时会自动删除或添加它。

如果您选择perl,请尝试以下操作:

perl -0777 -ne '
    $given = 3;     # an example of the given offset
    printf "character at offset %d = %s\n", $given, substr($_, $given, 1);
    $pos = rindex(substr($_, 0, $given), "\n", $given);
    if ($pos < 0) {
        print "not found\n";
    } else {
        printf "newline found at offset %d\n", $given - $pos - 1;
    }
' file

如果您更喜欢bash,则可以使用bash

file="./file"
given=3                               # an example of the given offset

str="$(xxd -ps "$file" | tr -d '\n')" # to the hexadecimal expression
for (( i=given; i>=0; i-- )); do
    j=$(( i * 2 ))
    c="${str:$j:2}"                   # substring offset j, length 2
    if [[ $c = "0a" ]]; then          # search for the substring "0a"
        printf "newline found at offset %d\n" $(( given - i - 1 ))
        exit
    fi
done
echo "not found"

该概念与perl版本相同。它首先将整个文件转换为十六进制表达式,并从给定位置开始向后搜索子字符串"0a"。

希望这可以帮助。