7

This question already has an answer here:

I try to run a series of commands as a whole inside the main shell, but the way I was teached only works inside the subshell:

🌐 echo $BASHPID
18884
🌐 (echo "hello $BASHPID";sleep 5;echo "hello again $BASHPID")
hello 22268
hello again 22268

I also tried:

. (echo "hello $BASHPID";sleep 5;echo "hello again $BASHPID")
source (echo "hello $BASHPID";sleep 5;echo "hello again $BASHPID")

to use the source command, because I learned it forces the script to run inside the main shell.

I guess, it would work, if I put the commands inside a file and run it with the source command, but I would like to know if there is a way beyound a script file.

marked as duplicate by Gilles bash May 22 '17 at 23:59

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 2
    What prevents you from doing echo "hello $BASHPID";sleep 5;echo "hello again $BASHPID" ? – Rui F Ribeiro May 22 '17 at 15:21
  • I would like to know if I can put the three commands into a block and execute this block inside the main shall, moreover I would like to know if it makes sense to want it or if it contradicts some shell logic. – sharkant May 22 '17 at 15:26
  • Maybe it would help if you you explain what your application goal is (I mean, it's a good thing to discover how shell mechanics work, but for which task do you want it and why you use () anyhow) – Philippos May 22 '17 at 15:28
  • my understanding of shell, subshells and jobs is too vague. I thought, maybe this could clarify it. for instance, my understanding of (echo "hi") is that it is run inside a subshell and (echo "hi")& is run inside a subshell, but the job is once again run in a further subshell. If there is a block command inside the shell to run the block inside the main shell then this block run as a job would run in a subshell, so basically the first job block is run inside the second subshell and the second job block is run inside the first. If this was true, I would had a less vague understanding. – sharkant May 22 '17 at 15:33
  • 1
    Try source with: source <(echo 'echo "hello $BASHPID";sleep .5;echo "hello again $BASHPID"') – F. Hauri May 22 '17 at 15:46
8

Instead of ( something ), which launches something in a subshell, use { something ; }, which launches something in the current shell

You need spaces after the {, and should also have a ; (or a newline) before the }.

Ex:

$ { echo "hello $BASHPID";sleep 5;echo "hello again $BASHPID" ; }
hello 3536
hello again 3536

Please note however that if you launch some complex commands (or piped commands), those will be in a subshell most of the time anyway.

And the "portable" way to get your current shell's pid is $$.

So I'd instead write your test as:

{ echo "hello $$"; sleep 5 ; echo "hello again $$" ; }

(the sleep is not really useful anyway here)

  • I get an error massage: – sharkant May 22 '17 at 15:34
  • 🌐 {echo "hello $BASHPID";sleep 5;echo "hello again $BASHPID"} No command '{echo' found, did you mean: Command 'aecho' from package 'netatalk' (universe) Command 'echo' from package 'coreutils' (main) {echo: command not found hello again 18884} – sharkant May 22 '17 at 15:34
  • 1
    and you should use: echo $$ instead, to have your current shell's pid, imo. – Olivier Dulac May 22 '17 at 15:36
  • 2
    space is needed only after opening '{ ' , and a semicolon before closing ';}' that's only if your codeblock in one line ! which mean a new line makes semicolon useless – Jonah May 22 '17 at 15:56
  • 1
    It's because { is a keyword. Same reason you need a space or other token delimiter ((, <, ... would also do) after while or for or [[. – Stéphane Chazelas May 22 '17 at 16:10
11

Depends what you mean by as a whole.

If you only mean send several commands to the shell, and make sure the shell doesn't start running them until you've entered them all, then you can just do:

cmd1; cmd2

Or

cmd1Ctrl+VCtrl+Jcmd2

(or enable bracketed-paste (bind 'set enable-bracketed-paste on') and paste the commands from a terminal that supports bracketed paste).

Or:

{
cmd1
cmd2
}

To have them on several lines.

If you want to group them so they share the same stdin or stdout for instance, you could use:

{ cmd1; cmd2; } < in > out

Or

eval 'cmd1; cmd2' < in > out

If you want them to run with their own variable and option scope, as bash doesn't have the equivalent of zsh anonymous functions, you'd need to define a temporary function:

f() { local var; var=foo; bar;}; f
  • this is very interesting:{ cmd1; cmd2; } < in > out – sharkant May 22 '17 at 16:06
  • @sharkant: You can't have better source of infos than Stephane, imo ^^ (and you should probably re-attribute the 'accepted answer' checkmark ! ). See also Stephane's answers on his profile ... lots to be learned (but maybe, for you, in a little while, after you read things such as the starting point urls I gave in a comment on your post?). (Gosh, I really sound like a fan, don't I ? ...) – Olivier Dulac May 22 '17 at 16:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.