Hi John, I'm a bit busy right now, so will reply properly later...
> A bash detail puzzles me. I used the (multi-)command in the bug > report and put sudo in front of it. That gives: > > $ sudo for FILE in $(dpkg-divert --list | grep nvidia-340 | awk '{print > $3}'); do > > dpkg-divert --remove $FILE > > done > bash: syntax error near unexpected token `do' > > It works OK when I put the command in a file. ...but that's an easy one. The shell's main purpose is to run programs, and it looks for those along the directories in the PATH environment variable, e.g. `ls' is found as /bin/ls and executed. Some things that may appear similar are built in to the shell because of Unix's design. `cd' is a key one. Each process has a current working directory and that can only be affected by the process. One can write a /bin/cd that processes its argument and calls chdir(2) but that changes the directory of the process running the /bin/cd executable and when the process exits, the change dies with it leaving the shell in the same working directory as before. So `cd' has to be a shell built in. Over time, other commands that can work as /bin/foo were also built in to some shells for speed. Control flow commands weren't initially built in to the shell as there was no room for the extra code. goto(1) used to manipulate the shell's file offset into the script after seeking out the corresponding :(1) label. As space became available, control flow moved into the shell. The `for' above is a shell keyword for the control-flow loop and is built in. type(1) is a shell built-in command that describes what the shell thinks of given names. $ type : : is a shell builtin $ type for for is a shell keyword $ type ls ls is aliased to `ls --color=auto' $ type diff diff is /usr/bin/diff $ type sudo sudo is hashed (/usr/bin/sudo) $ help(1) will give the man-page extract for a built-in in bash, e.g. `help time'. The shell is attempting to parse all of the entered line before running any of it. There's a grammar involved that's more complex than just every command is a run of words. `do' is a reserved word and only appears in grammar productions within a for-loop or similar. Here's an extract from http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html for_clause : For name do_group | For name sequential_sep do_group | For name linebreak in sequential_sep do_group | For name linebreak in wordlist sequential_sep do_group ; while_clause : While compound_list do_group ; until_clause : Until compound_list do_group ; do_group : Do compound_list Done ; %token If Then Else Elif Fi Do Done /* 'if' 'then' 'else' 'elif' 'fi' 'do' 'done' */ %token Case Esac While Until For /* 'case' 'esac' 'while' 'until' 'for' */ The shell cannot pass your command, which boils down to $ sudo for; do bar; done -bash: syntax error near unexpected token `do' $ `for' is only recognised as the start of a for_clause when it appears in the right place according to the grammar. Otherwise, `echo for ever' wouldn't work. Thus the `sudo for ...' means no for-loop has been started and `do' isn't valid at the start of a command. sudo(1) is an example of a command that takes another command to run as its parameters. env(1) and nice(1) are others. sudo isn't built in to the shell and passes its arguments to execve(2) for the kernel to overwrite the sudo executable in the current process with the new one given in the arguments. There is no /bin/for executable so the approaches are to use a script, as you did, or to have sudo run a shell and specify the shell commands as a parameter. $ sudo sh -c 'for f in 3 1 4; do >/tmp/$f; done' [sudo] password for ralph: $ ls -l /tmp/{3,1,4} -rw-r--r-- 1 root root 0 Mar 1 11:21 /tmp/1 -rw-r--r-- 1 root root 0 Mar 1 11:21 /tmp/3 -rw-r--r-- 1 root root 0 Mar 1 11:21 /tmp/4 $ This method requires quoting the arguments correctly so interpretation of their contents happens either by the shell running sudo, or the shell run by sudo, as desired. Also, here I used sh(1) as it was adequate. bash(1) might sometimes be needed. In the case of your original command above, the sudo isn't required for the first dpkg-divert(1) or awk(1), so it could have been for FILE in $(dpkg-divert --list | grep nvidia-340 | awk '{print $3}'); do sudo dpkg-divert --remove $FILE done though that would add some overhead if the loop's body was run many times. -- Cheers, Ralph. -- Next meeting: BEC, Bournemouth, Tuesday, 2019-03-05 20:00 Check to whom you are replying Meetings, mailing list, IRC, ... http://dorset.lug.org.uk/ New thread, don't hijack: mailto:dorset@mailman.lug.org.uk