在命令行中,我可以使用gsutil cat将输入通过管道传递到sox。完美运作。这是从命令行运行的代码:
sox <(gsutil -q cat gs://some-bucket/201808130800.wav) ./test-show.wav -t
wav trim 0 10
但是,在我的bash脚本中,它失败了。我尝试了许多配置。一天结束时,sox抱怨文件名不正确。相同的确切代码从命令行运行。
这是代码行。所有变量都是正确的。如果我使用sox来处理本地文件,那么效果很好。
sox $(for ((i=0; i<file_count; i++)); do
file_time=$(date "+%s" --date="$(date -d @$base_time) +$i hours")
file_name=$(date --date="@$file_time" "+%Y%m%d%H%M")
echo -e "<(gsutil -q cat gs://somebucket/$file_name.wav)"
done) "$target/$show_name.wav -t wav trim $f_offset $run_time"
如果我在命令前添加回显,则屏幕上显示的内容恰好是从命令行执行的内容。我可以将输出复制并粘贴到命令行,并且可以正常工作。
以下是输出:
sox FAIL formats: can't open input file `gs://somebucket/201808130800.wav)': No such file or directory
任何帮助将不胜感激。
附录:
我几乎可以和它一起工作:
gsutil cat "$(for ((i=0; i<file_count; i++)); do
file_time=$(date "+%s" --date="$(date -d @$base_time) +$i hours")
file_name=$(date --date="@$file_time" "+%Y%m%d%H%M")
echo "gs://somebucket/$file_name.wav"
done)" | sox -V4 - $target/$show_name.wav trim $f_offset $run_time
答案 0 :(得分:3)
shell不会将命令替换(封装$(...)
循环的for
语法)的输出解析为代码;相反,它仅通过字符串分割(在空格上分割,除非IFS
有所更改)和全局扩展(用匹配列表替换*.txt
之类的字符串)后才直接放在命令行上到sox
。
但是,<(...)
不是sox
的指令; process substitution指示 shell 放置一个文件名,在调用sox
之前,该文件名可从命令行读取该文件名以检索子进程的输出。
您可以通过以下几种方式动态生成与命令输出关联的文件名。
这里要做的一件更容易的事情是建立显式(命名)FIFO,而不是依靠进程替换来创建匿名FIFO:
#!/usr/bin/env bash
tempdir=$(mktemp -d "${TMPDIR:-/tmp}/sox-pipes.XXXXXX") || exit
declare -a fifos=( )
cleanup() {
rm -rf "$tempdir"
kill "${!fifos[@]}"
}
trap cleanup EXIT
for ((i=0; i<file_count; i++)); do
file_time=$(date "+%s" --date="$(date -d @"$base_time") +$i hours")
file_name=$(date --date="@$file_time" "+%Y%m%d%H%M")
mkfifo "$tempdir/$file_name.fifo"
gsutil -q cat gs://some-bucket/$file_name.wav >"$tempdir/$file_name.fifo" &
fifos[$!]="$tempdir/$file_name.fifo"
done
sox "${fifos[@]}" ./test-show.wav -t
可以对这种方法进行改造,使其可以在任何符合POSIX的外壳程序上使用-严格要求不使用数组-这意味着它也可以在完全不支持<(...)
语法的外壳程序上使用。
eval
命令棘手的事情是,必须非常小心地进行操作,以防止将数据(如文件名)用于shell注入攻击。请注意,使用printf %q
来转义被替换为字符串的数据。
#!/usr/bin/env bash
cmdline=''
for ((i=0; i<file_count; i++)); do
file_time=$(date "+%s" --date="$(date -d @"$base_time") +$i hours")
file_name=$(date --date="@$file_time" "+%Y%m%d%H%M")
printf -v piece ' <(gsutil -q cat gs://some-bucket/%q.wav) ' "$file_name"
cmdline+="$piece"
done
eval "sox ${cmdline} ./test-show.wav -t"
这里有一些棘手的警告:gsutil
实例要等到它们的stdout描述符的所有副本关闭后才能退出,这意味着它们在{{1 }}完成,直到shell关闭自己的副本为止。
sox
答案 1 :(得分:0)
在OP中,先前已将其编辑为问题:
这是行之有效的最终解决方案。首先,我从wav源文件更改为原始PCM文件,以删除44字节的wav标头。这使餐饮变得无缝。这是一个班轮:
gsutil -q cat $(for ((i=0; i<file_count; i++)); do file_time=$(date "+%s" --date="$(date -d @$base_time) +$i hours") file_name=$(date --date="@$file_time" "+%Y%m%d%H%M") echo "gs://somebucket/$file_name.raw" done) | sox -t raw -b 16 -c 2 -e signed-integer -r 48k -L - $target/$show_name.wav trim $f_offset $run_time