bash 如果通过 /bin/sh -c 运行,则命令无法按预期工作

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14639001/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 04:24:32  来源:igfitidea点击:

command not working as expected if run via /bin/sh -c

linuxbashshell

提问by shaurya

I have to concatenate a set of files. Directory structure is like this:

我必须连接一组文件。目录结构是这样的:

root/features/xxx/multiple_files... -> root/xxx/single_file

what i have written (and it works fine):

我所写的(它工作正常):

for dirname in $(ls -d root/features/*|awk -F/ '{print $NF}');do;mkdir root/${dirname};cat root/features/${dirname}/* > root/${dirname}/final.txt;done

for dirname in $(ls -d root/features/*|awk -F/ '{print $NF}');do;mkdir root/${dirname};cat root/features/${dirname}/* > root/${dirname}/final.txt;done

But when i run the same thing via sh shell

但是当我通过 sh shell 运行同样的事情时

/bin/sh -c "for dirname in $(ls -d root/features/*|awk -F/ '{print $NF}');do;mkdir root/${dirname};cat root/features/${dirname}/* > root/${dirname}/final.txt;done"

/bin/sh -c "for dirname in $(ls -d root/features/*|awk -F/ '{print $NF}');do;mkdir root/${dirname};cat root/features/${dirname}/* > root/${dirname}/final.txt;done"

it gives me errors:

它给了我错误:

/bin/sh: -c: line 1: syntax error near unexpected token `201201000'
/bin/sh: -c: line 1: `201201000'

My process always appends /bin/sh -c before running any commands. Any suggestions what might be going wrong here? Any alternate ways? I have spent a really long time on this ,without making much headway!

我的进程总是在运行任何命令之前附加 /bin/sh -c 。任何建议这里可能会出错?有什么替代方法吗?我在这方面花了很长时间,但没有取得太大进展!

EDIT: `ls -d root/features/*|awk -F/ '{print $NF}'returns

编辑: `ls -d root/features/*|awk -F/ '{print $NF}'返回

201201
201201000
201201001
201201002
201201003
201201004
201201005
201201006
201201007
201202000
201205000
201206000
201207000
201207001
201207002

采纳答案by djjolicoeur

I got this to run in the little test environment I set up on my box. Turns out it didn't like the double quotes. The issue I ran into was the quotes around the awk statement...if you wrap it in double quotes it prints the whole thing.....I used cut to get the desired result, but my guess is you'll have to change the -f arg to 3 instead of 2..I think.

我让它在我在我的盒子上设置的小测试环境中运行。原来它不喜欢双引号。我遇到的问题是 awk 语句周围的引号......如果你用双引号将它包装起来,它会打印整个内容......我使用 cut 来获得所需的结果,但我的猜测是你必须将 -f arg 更改为 3 而不是 2..我认为。

/bin/sh  -c 'for dirname in $(ls -d sh_test/* | awk -F/ '\''{print $NF}'\''); do mkdir sh_test_root/${dirname}; cat sh_test/${dirname}/* > sh_test_root/${dirname}/final.txt;done'

edit: Tested edit proposed by nadu and it works fine. The above reflects that change.

编辑: nadu 提出的经过测试的编辑,效果很好。以上反映了这种变化。

回答by nadu

Always use sh -c 'cmd1 | cmd2'with single quotes.

始终sh -c 'cmd1 | cmd2'与单引号一起使用。

Always use sh -eu -xv -c 'cmd1 | cmd2'to debug.

总是sh -eu -xv -c 'cmd1 | cmd2'用来调试。

Always use bash -c 'cmd1 | cmd2'if your code is Bash-specific (cf. process substitution, ...).

bash -c 'cmd1 | cmd2'如果您的代码是特定于 Bash 的,请始终使用(参见进程替换,...​​)。

Remove ;after doin for ... ; do; mkdir ....

;doin之后删除for ... ; do; mkdir ...

Escape possible single quotes within single quotes like so: ' --> '\''.

逃生可能的单引号内的单引号,像这样: ' --> '\''

(And sometimes just formatting your code clarifies a lot.)

(有时只是格式化您的代码会澄清很多。)

Applied to your command this should look somewhat like this ...

应用于您的命令,这应该看起来像这样......

# test version
/bin/sh -c '
   for dirname in $(ls -d /* | awk -F/ '\''{print $NF}'\''); do
     printf "%s\n" "mkdir root/${dirname}";
     printf "%s\n" "cat root/features/${dirname}/* > root/${dirname}/final.txt"; 
     echo
   done
' | nl

# test version using 'printf' instead of 'ls'
sh -c '
   printf "%s##代码##0" /*/ | while IFS="" read -r -d "" file; do
     dirname="$(basename "$file")"
     printf "%s\n" "mkdir root/${dirname}";
     printf "%s\n" "cat root/features/${dirname}/* > root/${dirname}/final.txt"; 
     echo
   done
' | nl