运行 容器中的原始命令 shell VS 命令在 shell 中使用 /bin/bash -ac,没有给出相同的结果

Running raw command in container shell VS command using /bin/bash -ac in shell, not giving the same result

当做 docker exec -it [container_id] /bin/bash:

我运行下面是:
请假设文件夹 test/data 存在。

root@6f200d5b9691: for file in $(aws s3 ls s3://foo-bucket | awk '{print $NF}') ; do aws s3 cp foo-bucket/${file} test/data && tar -xzf test/data/${file} -C test/data/ ; done

为此,我把相关路径下的所有文件都弄出来解压了!

但是,当以这种方式通过 shell 执行完全相同的操作时:
root@6f200d5b9691: /bin/bash -ac "for file in $(aws s3 ls s3://foo-bucket | awk '{print $NF}') ; do aws s3 cp foo-bucket/${file} test/data && tar -xzf test/data/${file} -C test/data/ ; done"

我收到奇怪的错误,例如:
/bin/bash: -c: line 1: syntax error near unexpected token foo1.tar.gz' /bin/bash: -c: line 1: foo1.tar.gz ; do aws s3 cp s3://foo-bucket/foo1.tar.gz 'test/data' && tar -xzf 'test/data/foo1.tar.gz' -C 'test/data/' ; done'

知道为什么会这样吗? 我做错了什么?

通过 /bin/bash -ac "[commands here]" 到 运行 的第二个选项很重要,因为我想通过如下命令使用它:
docker-compose run [service_name] /bin/bash -ac "[...]"

如果你运行

bash -c "file=foo; echo $file"

未打印任何内容,因为 $filebash -c 看到它之前已展开(无)。

解决方法是使用单引号:

bash -c 'file=foo; echo "$file"'

现在,bash -c 可以看到整个参数未修改。

对于你的情况,这意味着

bash -ac 'for file in $(aws s3 ls s3://foo-bucket | awk "{print $NF}"); do
    aws s3 cp foo-bucket/"$file" test/data \
        && tar -xzf test/data/"$file" -C test/data/
done'

整个字符串用单引号引起来。这允许正确引用 $file;对于 awk 命令,我们必须转义 $NF 因为我们使用双引号。另一种选择是

awk '\''{print $NF}'\''

将单引号转换为单引号字符串。

$() 和 $var inside double quota 在执行命令之前解析。 所以 $file 不会在 new bash 内解析,而是在将字符串发送到 bash 之前解析。所以我相信 $file 在这种情况下会是空的。