如何仅在命令的第一部分使用 forloop

如何仅在命令的第一部分使用 forloop

我有这个.sh:

#!/bin/bash

cd /home/pi/Desktop/PiFmRds/src

for f in $(ls -1 /home/pi/Desktop/Music/Acapella/*.mp3|sort -R); do sox -t mp3 "$f" -t wav -r 44100 - | sudo ./pi_fm_rds -freq 102.1  -audio - -ps WZSFM -rt "ZSFM"; done

基本上,它以随机顺序播放目录中的所有文件,将它们即时从 mp3 转换为 wav,然后通过管道将其传输到 pifm。 Pifm 是一个可以在调频广播上播放的节目。问题是,由于我使用的是 forloop,因此每个文件之间的无线电上都有一些静态,因为它运行 pifm 命令来广播每个单独的文件,因此在 pifm 命令之间会产生一些静态在一个文件后停止并开始处理下一个文件。有没有办法让 forloop 只在命令的第一部分进行,它会选择随机歌曲,而不是在实际广播的 pifm 部分?这将使 pifm 命令始终处于广播状态,而 forloop 仅适用于命令在文件中进行混洗的部分。

另外,我尝试了这个:

#!/bin/bash

cd /home/pi/Desktop/PiFmRds/src

    for f in $(ls -1 /home/pi/Desktop/Music/Acapella/*.mp3|sort -R); do sox -t mp3 "$f" -t wav -r 44100 - ; done | sudo ./pi_fm_rds -freq 102.1  -audio - -ps WZSFM -rt "ZSFM"

但播放完一个文件就停止了。

编辑:我将问题总结如下:文件之间存在静态的原因是因为 pifm 命令正在广播每个文件,但对于 forloop 的每次迭代都会停止并重新运行,因此迭代之间只有静态。我怎样才能让它单独循环浏览每个文件(有点必要,否则我最终会遇到更多问题),但仍然让它一直广播?

答案1

首先,阅读并理解为什么不解析 ls (以及该怎么做)?- 有充分的理由说明为什么您不应该ls像您尝试那样使用。

如果您不需要随机化播放列表,我会说“只需使用find及其-exec选项。因此,相反,使用find-print0选项,将输出通过管道传输到sort -z -R(或shuf -z),然后xargs -0r运行脚本来转换和播放文件。

首先创建一个像这样的脚本:

#!/bin/bash

pifm='/home/pi/Desktop/PiFmRds/src/pi_fm_rds'

for f in "$@"; do
  sox -t mp3 "$f" -t wav -r 44100 -
done | sudo "$pifm" -freq 102.1  -audio - -ps WZSFM -rt "ZSFM"

这与您的原始脚本几乎相同,只是它将所有文件名作为命令行参数而不是尝试解析ls.它使用 sox 将作为参数传递给它的所有音频文件名转换为 WAV 格式,并将输出通过管道传输到pi_fm_rds.

顺便说一句,您应该通过添加额外的换行符和缩进来努力使脚本更具可读性。您可以通过\在行尾添加反斜杠 () 来告诉 bash 下一行是当前行的延续。或者,如下面的下一个脚本所示,以竖线字符 ( |) 结尾的行也会在下一行中继续,而无需\.

您需要进行配置,sudo以允许用户/home/pi/Desktop/PiFmRds/src/pi_fm_rds以 root 身份运行而无需密码(如果您尚未这样做)。

将其另存为,例如 /usr/local/bin/randomise-playlist.sh 并使其可执行chmod +x /usr/local/bin/randomise-playlist.sh

然后像这样运行它以向其传递要转换和播放的文件名列表:

find /home/pi/Desktop/Music/Acapella/ -type f -name '*.mp3' -print0 |
  sort -z -R |
  xargs -0 -r /usr/local/bin/randomise-playlist.sh

或者,使用- shufshuf -z代替sort -z -R- shuf 的目的是随机化其输入行。

因为这在整个管道中使用 NUL 作为文件名分隔符,所以它将使用包含任何有效字符(甚至空格、制表符、换行符和 shell 元字符,如&;)的文件名。

顺便说一句,有些人喜欢总是用 结束连续行\,并将管道|字符(或||&&或其他字符)放在下一行的开头。两种方法的工作原理完全相同,因此请选择对您来说更易读的方法。但用几个空格缩进连续的行总是一个好主意(我通常使用 2 个空格。4 个空格也不错。或者有时我尝试排列参数,以便它们在后续行的同一列中开始)或制表符以表明它是上一行的延续。

find /home/pi/Desktop/Music/Acapella/ -type f -name '*.mp3' -print0 \
  | sort -z -R \
  | xargs -0 -r /usr/local/bin/randomise-playlist.sh

以上所有内容都需要 GNU 版本的findxargssort(或shuf)。由于您在 Raspberry Pi 上运行,因此 GNUcoreutils应该findutils是标准的。

如果需要,可以将整个find ... | xargs命令放入其自己的脚本中 - 每次运行它时都需要键入,这有点长。

答案2

不要使用 for 循环单独播放每个片段,而是使用 sox 命令的功能将它们连接到单个播放流中。

另请注意,如果文件名中存在特殊字符(包括空格),则生成列表的给定方法将中断。此外,如果您有大量文件,则可能会达到命令行长度限制。由于您原来的命令会遇到这两个问题,因此我不会完全解决这个问题。但一个可能的解决方案是以随机顺序将文件名映射到新编号的文件名(可能作为符号链接),然后将它们作为单个名称提供给 sox,-t mp3 "*.mp3"并允许 sox 进行全局扩展。

以下更改应该可以满足您的需求:

  • 将文件列表直接交给sox,而不是循环运行sox

  • 删除该-t mp3选项并让 sox 自动检测文件类型

  • 确保所有 .mp3 文件使用相同的采样率和通道数,以便它们可以组合成单个串联输出流

    sox $(ls -1 /home/pi/Desktop/Music/Acapella/*.mp3|sort -R) -t wav -r 44100 - | sudo ./pi_fm_rds -freq 102.1 -音频 - -ps WZSFM -rt“ZSFM”

如果上述任何操作失败,您可能需要在合并文件之前对文件进行预处理。

您的第二次尝试失败了,因为默认情况下音频文件有一个标头,并且无法直接附加。如果不检查 pi_fm_rds 命令的文档,我不知道它是否需要该标头。如果它没有预料到,这可能是文件之间静态的来源。在这种情况下,另一种方法是告诉 sox 发出没有标头的原始音频,并继续在 for 循环中为每个文件单独调用它。

相关内容