我有一个youtube-dl
命令,运行时会输出两行新行,我试图将每一行捕获到变量中。
这是我目前所得到的,但由于某种我无法理解的原因,它只捕获第一行输出而丢弃第二行($audio
为空)。
read -r video audio <<< "$(youtube-dl -g --youtube-skip-dash-manifest https://www.youtube.com/watch?v=-gcvAxJJiGo)"
我该如何修复它以使其捕获两个输入,或者是否有更可靠的方法来做到这一点?
答案1
read -r video audio
将捕获第一个 ($IFS 分隔)单词进入“视频”和其余的进入“音频”
捕获两行的另一种方法是使用命令分组和进程替换:
{
read -r video
read -r audio
} < <(
youtube-dl -g --youtube-skip-dash-manifest "$url"
)
之间的空格< <(
很重要:a<(process substitution)
就像一个文件,所以您可以使用它<
来重定向来自文件的输入。
我突然想到:另一种阅读方式输出行数是个mapfile
命令:
mapfile -t lines < <(youtube-dl ...)
declare -p lines # just to inspect the array contents
video=${lines[0]}
audio=${lines[1]}
答案2
read
按设计读取一行。读两行最简单的方法是使用read
两次。
exec 3< <(youtube-dl -g --youtube-skip-dash-manifest 'https://www.youtube.com/watch?v=-gcvAxJJiGo')
IFS= read -r -u3 video
IFS= read -r -u3 audio
exec 3>&-
read -u
不可移植,既不是<(…)
。它们在 Bash 中工作,并且您的问题被标记狂欢,因此它们应该适合您。
注意,我使用单引号括住包含的字符串?
,以防您有一个目录https:/www.youtube.com/
和一个匹配的文件。这仅仅是有可能的,但仍然引用不应被视为通配符的通配符在任何 shell 中都是很好的做法。比较Zsh 中发生了什么。
空的IFS
总体来说是正确的,当您想读一行时。
其他答案不用exec
,很好。这是我的个人喜好在工具使用之前直观地设置输入(比较这个答案)。不幸的是,<(…) { …; }
不起作用。有了exec
我可以做到。
或者我可以通过管道传输youtube-dl
到脚本的其余部分来完成此操作。
youtube-dl -g --youtube-skip-dash-manifest 'https://www.youtube.com/watch?v=-gcvAxJJiGo' \
| {
IFS= read -r video
IFS= read -r audio
}
通常,Bash 在子 shell 中运行{ … }
片段,因此变量在 之后将不复存在}
。shopt -s lastpipe
可以更改此设置(如果作业控制未处于活动状态)。如果没有lastpipe
(不可移植),整个方法是可移植的,但您需要运行使用{ }
块内变量的所有内容,因为您不知道它是否是子 shell(POSIX 允许这两种行为)。请注意,块内的 stdin 来自youtube-dl
;如果块内的任何内容需要使用整个脚本的 stdin,那么您可能需要使用 来exec
处理描述符。
另一种可移植的方法是这样的:
input="$(youtube-dl -g --youtube-skip-dash-manifest 'https://www.youtube.com/watch?v=-gcvAxJJiGo')"
video="$(printf '%s\n' "$input" | head -n 1)"
audio="$(printf '%s\n' "$input" | head -n 2 | tail -n 1)"
您可以使用或来代替head
和来隔离右线。tail
sed
awk
请注意,此解决方案不使用read
。如果您需要的功能,那么这不是办法。不过,在这种特殊情况下,read
您似乎不需要的功能。read
即使youtube-dl
非常快,我也不建议video="$(youtube-dl … | head -n 1)"
加上audio="$(youtube-dl … | …)"
。如果video
和audio
来自同一个input
,来自 的同一个 调用youtube-dl
,那么它们就与工具允许的一样连贯。否则它们可能不连贯。坦率地说,我不知道它们可能有多不连贯(如果有的话);但我知道一般来说最好查询一次(例如date +%H:+%M
应该始终连贯;date +%H; date +%M
如果你在整整一个小时之前运行它,你会感到惊讶)。