外壳语言

外壳语言

长话短说

我对此进行了研究,您可以看到我在更多细节和我的尝试部分。

我的命令是

date && echo "hi 1" && echo "1/0" | bc >/dev/null 2>&1 && echo "hi 2" && date +'%s'

我期望/想要的输出是这样的

Tue, Nov 17, 2020  3:13:49 PM
hi 1

并让它停止。我的想法是表达式的“ echo/ bc”部分(假定操作优先级由括号表示)

<part-before> echo "1/0" | bc >/dev/null 2>&1 && <part-after>

应该做到这一点,这样<part-after>就不会达到目的。然而,我的实际的输出(使用date现在的命令)是

Tue, Nov 17, 2020  3:13:49 PM
hi 1
hi 2
1605651229

问题:如何让它失败并在被零除问题处停止而不输出任何内容?


(此后的任何内容都是为了提供更多详细信息。)

快速说明:即使没有重定向,被零除失败也不会停止链&&,即短路的“与”运算符链。看哪,

bballdave025@MACHINE ~
$ date && echo "hi 1" && echo "1/0" | bc && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:14:47 PM
hi 1
Runtime error (func=(main), adr=3): Divide by zero
hi 2
1605651287

bballdave025@MACHINE ~
$

我想使用这个bc方法,主要是为了避免像$(( ))下面这样的bash-isms。 (说到 bash-isms,尝试&>/dev/null并没有改变任何东西。)我想得到像下面的例子这样的结果

bballdave025@MACHINE ~
$ date && echo "hi 1" && echo "$((1/0))" >/dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:15:17 PM
hi 1
-bash: 1/0: division by 0 (error token is "0")

bballdave025@MACHINE ~
$

但我不希望出现错误。只要除以零的错误结果没有输出,即没有 -ed (这违背了重定向和echo的目的),我就可以消除错误stdoutstderr

### NOT WHAT I WANT, other than what is output ###
bballdave025@MACHINE ~
$ date && echo "hi 1" && ((1/0)) >/dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:15:44 PM
hi 1

bballdave025@MACHINE ~
### NOT WHAT I WANT, other than what is output. ###

更理论上的说明:某物正在将 0(零)传递给&&;它是什么?

我已经找到了这个来源已归档),其中指出

重定向不是命令。

我现在不知道该去哪里寻找。


编辑: 我又做了一些研究。我想补充两点。

  1. 我找到了几个来源。其中之一已归档)我认为可能是关键,但我仍然不明白它是如何应用的。其他已归档)描述了短路,我认为这可能很重要。

  2. >/dev/null当我不小心替换为时,我发现了一个奇怪的行为>dev/null,从而引入了错误除零问题。这是我所看到的,

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:41:39 PM
hi 1
-bash: dev/null: No such file or directory

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:41:48 PM
hi 1
-bash: dev/null: No such file or directory

bballdave025@MACHINE ~
$

老实说,这让我更加困惑,特别是考虑到第一个,可能是关键的来源。


更多细节和我的尝试

我试图理解&&链和重定向,/dev/null以便建立一个包含更大可执行文件的更长链。我也想了解命令列表分隔符已归档)更好,一般来说,无论这些可执行文件如何与 一起工作&&,我真的很想了解这里发生了什么。

在继续我的真实示例之前,我一直在研究这个玩具示例。我一直在尝试“向后努力”,看看是否能找到问题所在。也许这对于解决问题会有用。

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >/dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:16:41 PM
hi 1
hi 2
1605651401

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >/dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:16:52 PM
hi 1
hi 2
1605651412

## LET'S SEE WHAT GETS SENT TO /dev/null

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >abc 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:17:06 PM
hi 1
hi 2
1605651426

bballdave025@MACHINE ~
$ cat abc
1

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >abc 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:17:18 PM
hi 1
hi 2
1605651438

bballdave025@MACHINE ~
$ cat abc
Runtime error (func=(main), adr=3): Divide by zero

## LET'S TRY GIVING IT ANOTHER ERROR RIGHT AFTER

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:24:35 PM
hi 1
-bash: dev/null: No such file or directory

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:25:00 PM
hi 1
-bash: dev/null: No such file or directory

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >abc 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:25:14 PM
hi 1
hi 2
1605651914

bballdave025@MACHINE ~
$ cat abc
1

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >abc 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:25:24 PM
hi 1
hi 2
1605651924

bballdave025@MACHINE ~
$ cat abc
Runtime error (func=(main), adr=3): Divide by zero

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" | bc >abc/def 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:25:41 PM
hi 1
-bash: abc/def: Not a directory

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" | bc >abc/def 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:25:46 PM
hi 1
-bash: abc/def: Not a directory

## GO BACK WITHOUT `bc` and without redirection

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" >/dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:29:03 PM
hi 1
hi 2
1605652143

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" >/dev/null 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:29:12 PM
hi 1
hi 2
1605652152

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" >abc 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:29:21 PM
hi 1
hi 2
1605652161

bballdave025@MACHINE ~
$ cat abc
1/1

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" >abc 2>&1 && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:29:29 PM
hi 1
hi 2
1605652169

bballdave025@MACHINE ~
$ cat abc
1/0

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$b" && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:30:09 PM
hi 1
1/1
hi 2
1605652209

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "$a/$c" && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:30:14 PM
hi 1
1/0
hi 2
1605652214

bballdave025@MACHINE ~
$ a=1; b=1; c=0; date && echo "hi 1" && echo "hi 2" && date +'%s'
Tue, Nov 17, 2020  3:30:26 PM
hi 1
hi 2
1605652226

bballdave025@MACHINE ~
$

这个SO交换已归档) 可能有一些有用的东西。

答案1

即使没有重定向,被零除失败也不会停止 && 链,即短路的“and”运算符链。

GNU BC:

$ echo "1/0" | bc 
Runtime error (func=(main), adr=3): Divide by zero
$ echo $?
0

忙碌盒:

$ echo "1/0" | busybox bc 
bc: divide by zero
$ echo $?
1

POSIX BC:

名称
bc - 任意精度算术语言
退出状态
应返回以下退出值:
0 -- 所有输入文件均已成功处理。
未指定——发生错误。

基本原理
由于以下几个原因,错误条件的退出状态未指定:

  • bc 实用程序可用于交互式和非交互式情况。不同的退出代码可能适合这两种用途。
  • 目前尚不清楚何时应给出非零退出;被零除、未定义的函数和语法错误都是可能的。
  • 目前尚不清楚退出状态有什么用处

它不会破坏链条,因为您的 bc 不认为除以零值得返回错误的退出状态。

不过,如果 GNU bc 无法读取输入文件,它确实会返回错误的退出状态。

我想使用 bc 方法,主要是为了避免像后面的 $(( )) 这样的 bash-isms。

$(( ))不是Bashism,而是Bashism的一部分POSIX shell 语言(算术扩展)


我不确定这里的上下文是什么,除以零与条件和&&链有何关系,以及您最终想要做什么,所以很难提供任何建议。

依赖于检测除以零的另一种方法是在首先尝试除法之前检查除数是否为零。

答案2

这是 XY 问题、有关管道、输出和除法的问题的混合。我会尽力澄清它们,尽管我觉得没有一个“单一”的问题需要回答。

外壳语言

分组

echo "before" && echo inside1 && echo inside2 && echo "after"

将多个语句分组的方法是使用圆括号或大括号:

echo "before" && ( echo inside1 && echo inside2 ) && echo "after"
echo "before" && { echo inside1 && echo inside2; } && echo "after"

()将在子shell中运行,而{ }只是在当前环境中形成一个列表。两者都可用于重定向该部分的输出。

echo "before" && ( echo inside1 && echo inside2 ) > /dev/null && echo "after"
echo "before" && { echo inside1 && echo inside2; } > /dev/null && echo "after"

返回值

每个命令完成后都会返回一个值。它保存在$?,因此您可以使用echo $? 执行后立即

约定返回零表示成功,非零表示不成功。无论该计划可能被认为是成功的。 and&&和 or ||shell 运算符就是利用这个概念。还有一些方便的小型实用程序,称为true和 ,false它们总是分别返回零(成功)和非零(失败)。

if true && false; then
 echo Yes
else
 echo No
fi

当您链接一系列 时&&,只有当左手返回成功时,运算符的右手才会执行。

例如,您可能想要创建一个名为 的文件夹photos并将所有文件移至其中。

你首先可以尝试使用

mv *.jpg photos

但如果只有一张照片,并且没有photos文件夹,它会将照片重命名为photos.

如果文件夹创建成功,您只能用于&&移动图像:

mkdir photos && mv *.jpg photos

如果mkdir失败,则不mv执行。

之所以如此,是因为失败时mkdir将返回非零值。

但是,如果该文件夹photos已经存在,mkdir也会返回错误条件(它无法创建文件夹)。

因此,更复杂的检查可能是:

( test -d photos || mkdir photos ) && mv *.jpg photos/

如果 photos 已经是一个目录,它不会尝试创建它,而是跳转到移动图像。然而,如果photos不是目录,但它能够创建它,它也会成功并移动照片。

如果照片不是文件夹(或文件夹的符号链接)并且未创建它,则左侧部分&&将失败并且照片不会移动。

旁注:对于这个特定的示例,您可以使用更简单的mkdir -p photos && mv *.jpg photos/,因为-pflag 使 mkdir 创建中间文件夹,并且如果文件夹已经存在则不会退出,但它不太说教。

管道

当您有管道时,默认情况下其返回值是最右边的命令之一。如果你执行

command1 | command2 | command3

整个管道的返回值将是 的返回值command3command1command2返回非零并不重要 。这可以通过设置选项来更改pipefail,以便返回值将是以下之一管道中返回非零的最右边的命令

被零除

在您的情况下,echo 1/0 | bc不会停止管道,因为bc确实返回 0 代码,即使它在屏幕上提供了有关尝试除以零的诊断。

直接执行$((1/0))是有问题的,因为错误发生在扩展上。您可以将其包装在新的 shell 进程中:

echo hi1 && bash -c 'echo $((1/0))' 2> /dev/null && echo hi2

甚至只是一个子外壳

echo hi1 && ( echo $((1/0))' ) 2> /dev/null && echo hi2

其实也有基本的解决办法检查除数是否为零。例如:

division() {
  if [ "$2" -eq 0 ]; then
     return 1
  fi
  echo "$(( $1 / $2 ))"
}

echo hi1 && division 1 0 && echo hi2

您甚至可以使用函数执行类似的操作bc(不过,它会输出 0 而不是空):

define division(x,y) {
   if (y == 0) return;
   return x/y;
}
division(1,0)

相关内容