DISH(1)
dish(1) β the manual page, but make it cute π
User Commands Β· dish
β¨ NAME β¨
dish β a tool for parallel sysadmin of multiple hosts π₯οΈπ
π¦ SYNOPSIS π¦
dish [option]... -e command {-g hosts_file | host_1 host_2 ...}
dicp {-g hosts_file | -g "user@host_1 ..."} local_file :remote_file
dicp {-g hosts_file | -g "user@host_1 ..."} :remote_file local_file
πΈ DESCRIPTION πΈ
okay so bestie, dish β the diligence shell β runs commands on a whole bunch of hosts at once via ssh/rsh/telnet, and it also makes distributing files with scp/rcp super easy, plus remote password changes and all that. and yes, it can process hosts in parallel mode. iconic behavior fr fr π
π OPTIONS π
-hβ prints a help message that briefly describes all the command-line options. short and sweet, we love a quick recap π-H,--helpβ the comprehensive help, examples included. it's giving full course meal β¨--versionβ prints the program version and the copyright message, then dips out. bye π-Vβ shows the version number and exits. same energy, less lore.-C <dir>β configuration directory, and$HOME/.dishis the default. inside this dir live the config fileshosts,rests,pass, andoptions. when you use it, it has to be the first argument in the command line string, or the second one if-Dwas chosen! alternatively you can just set the environment variableDISH_CONFand call it a day π-CC <dir>β same as-Cbut with a fallback to the default if the local config isn't found. meaning: if the filespass,optionsorrestsare missing from the directory you gave, but those files exist in$HOME/.dish, then the latter get considered instead. the one exception is$HOME/.dish/hostsβ that one gets ignored, periodt. using this option is equivalent to changing directory to the one you picked and then runningdish. π¦-c <name>β the program (aka the "connector") and its options used for connecting to the remote host(s) β for examplersh, where the spawned process will be"rsh $host <cmd>". your default connector isssh. bonus: by using a relevant text-based client as the connector, you can reach all kinds of hosts β switches, databases, and so on ππ-e <cmd>β the remote command to execute. can also be set by the environment variableDISH_CMD. π-E <cmd>β execute a command where the connection part is specified too, like"-E 'ssh $host date'", which is equal to"-e date". this option is incompatible with-cand-e. can also be set by the environment variableDISH_FUEXE. β-tβ force pseudo-tty allocation inssh. this happens automatically in the case of a password change. π-T <time>β timeout for command execution β default30s(per host). β°-TT <time>β total timeout for command execution β default300s(all hosts). this one is only useful when hosts are processed in sequence and the total processing time shouldn't exceed the specified upper bound. π°οΈ-x <regex>β regular expression for the shell prompt. this value says which prompt is to be expected in the program's shell after logging into a system bytelnet,mysql,sqlplusor other interactive command-line clients (see-c). the default value is(%|\$|#|\>) ?$. πΈ-X <regex>β regular expression for the password prompt. it's case-insensitive, with default valuePassword: *$. π-AD <regex>β regular expression for the ssh-prompt to add a new host key βconnecting (yes/no)?.*is the default. ποΈ-AC <str>β a string with the answer to the ssh-prompt to add a new host key βyesis the default. β-g <file>β file with the list of hosts/ip's/accounts to target; the command gets executed on these targets. the default host file is'$HOME/.dish/hosts'β normally one account per line, in the form"user@host"(ifsshis your connector of choice). to join lists, use the option repeatedly. alternatively, the environment variableDISH_HOSTScould be used to define the target hosts, and in that string they have to be separated by blanks. by combining this option with-ror-iyou can define various subsets of targeted hosts/accounts. π-r <file>β file with the list of resting hosts/accounts to exclude. the default one is'$HOME/.dish/rests'. a "resting host" means one that will be excluded from the targets. the list of resting hosts or the file name could also be specified by the environment variableDISH_RESTS. π΄-i <file>β file with the list of hosts/accounts to overlap with the targeted hosts. there's no default file. only overlapping hosts β ones in this list and at the same time defined as targets β will be processed. π―-u <name>β user name, default is your local user name. it can also be defined by the environment variableDISH_USER. internally the value is accessible by the variable$user(see examples). also it's irrelevant when accounts of the form"user@host"are processed, since those already include the user name. π-p <passwd>β login password (-p ""=-pp=-a0) β alias "login authentic" or "a0". if no authentication for login is required (no user- and no password-prompt appear), then use/dev/nullas the password. if the user name is requested, yet the password is an empty string, then/dev/emptyhas to be given as the password. the value of this option could also be a password file (see-P). eventually, you can define the password by the environment variableDISH_PASS. π-a <passwd>β additional password for authentication (-a ""=-aa=-a1) β alias "first authentic" or "a1". inside the spawn process, if a program likesmbmount,su,ssh, etc. asks for authentication, the a1-password is passed to it. this password can also be set by the environment variableDISH_PASS1. π«Ά-A <passwd>β one more password for authentication (-A ""=-AA=-a2) β alias "second authentic" or "a2". when a spawned process, after one authentication by the a1-password, asks again for a password, then a2 is sent. this password can also be set by the environment variableDISH_PASS2. β¨-n <passwd>β new password in case of password change (-n ""=-nn=-ne). π-p0β login without authentication β the same as-p /dev/null. πͺ-p1β set the a1-password to be the same as the login password. this option should not be used together with-p0and-a1. see example d) below. π-P <file>β file with password(s). the default password file is'$HOME/.dish/pass'. it must be readable only for the user (file mode600or700), otherwise the program exits with an error β but see also the next option. every line in the file can hold a password entry of the form:"password:username:hostname", alternatively"password:::username:::hostname", or"password;;;username;;;hostname". you can specify a list of hosts separated by the,or;characters. regular expressions for hostnames are also allowed (see the example configuration files in the distribution). π-mβ ignore the access permissions of the password file. rebel behavior, we respect it π-s [<time>]β sequential processing of hosts (default mode). if a time interval (measured in floating seconds) is specified, then the program waits this amount of time before starting to process the next host in the sequence. π’-Fβ spawn processes in background β fork and disconnect. this way all hosts are processed essentially in parallel! it's a very powerful option β depending on your RAM size and memory utilization, it shouldn't be a problem to process a few hundreds of hosts in parallel. anyway, be careful β if you have too many hosts on the list, you could put your system under load. the stdout's of the background processes are redirected to/dev/null, however you can use-lor-Lto write the output to files. see also 'bugs and known problems' in the manual page. π₯-fβ spawn processes in background without disconnecting from tty. it's the same as-F, whereas the stdout's of the spawned processes are sent to the terminal. also the parent process waits for his children to finish. see also 'bugs and known problems' in the manual page. π-qβ be quiet β skip output from spawn and login. when working with the secure shell, it's also convenient to usesshwith the-qoption. π€«-Qβ be QUIET β skip any output. absolute silence, main character energy π-vβ be verbose (default) β overrides-qand-Q. π’-l <file>β log command output to file. the output of the spawned processes is appended to the file. π-L <name>β write a separate log for every host, where<name>denotes the base name of the log file. the full name of a log file is defined as"<name>_<user@host>.log". ποΈ-jβ record the invoked command into a journal file with the name'$HOME/.dish/journal'. it keeps the history of the executed commands and their time of execution. an unique identifier is associated with every command. π-Jβ record the invoked command and the spawned processes as well. writes into the journal file the executed command as well as the single processes spawned and their time of execution. π§Ύ-o <file>β file with command line options passed to the program β default is'$HOME/.dish/options'. the options must be written in the file separated β one per line. by means of this file you can modify the standard configuration: set up fork mode to be default, change the default connector, and so forth. when working in "copy mode", i.e. by invoking the program asdicp,'$HOME/.dish/options.dicp'is considered to be the default options file. βοΈ-dβ enable expect's diagnostic output (look atman expect). π©Ί-Dβ Debug mode (dry-run). prints out environment variables, config file names, and commands to execute, then exits. this option should be used as first in the command line. π
β¨ EXAMPLES β¨
okay bestie, real talk before we dive in: the variables $host and $user are evaluated. meaning $host dynamically flips its value to the actual host/account name right before a new process gets spawned. same deal for $user. keep that in mind and everything below just makes sense. π«Ά
a) Check the date and uptime on hosts 192.168.0.1 and 192.168.0.2
dish -e 'date \; uptime' root@192.168.0.1 root@192.168.0.2
b) Distribute '.profile' and '.bashrc' to guest accounts on 'host1' and 'host2'
dish -E "scp $HOME/.profile $HOME/.bashrc guest@\$host:" host1
host2
or
dicp -e "$HOME/.profile $HOME/.bashrc guest@:" host1 host2
or
dicp -g "host1 host2" $HOME/.profile $HOME/.bashrc guest@:
or
dicp -g "guest@host1 guest@host2" $HOME/.profile $HOME/.bashrc :
so many paths to the same slay, pick your fave π
c) Copy remote '.profile' files into the local directory on localhost
dicp -g "guest@host1 guest@host2 admin@host2" :.profile
.profile.\$host
here's the tea: the name of the target file (the local file) will include the remote account name, so your local files get unique names. no collisions, no chaos. β¨
d) Use ssh to login on 'host1' and copy from there '.profile' to 'host2'
since the list of hosts can not be empty, a dummy host is used to initiate the process. the -t option is necessary to force pseudo-tty allocation in ssh β otherwise ssh will fail with an error on login. and a second password (a1-password) is required for scp-authentication on 'host2':
dish -a '' -E 'ssh -t user1@host1 scp .profile user2@host2:'
dummy_host
in case the password of 'user1' and 'user2' is the same, you'll only get asked once for a login password for user1@host1 if you use -p1:
dish -p1 -E 'ssh -t user1@host1 scp .profile user2@host2:'
dummy_host
or, equivalently and more simply:
dish -p1 -t -e 'scp .profile user2@host2:' user1@host1
e) Substitute lines with START_XNTPD=' by START_XNTPD="yes"' in /etc/rc.config
this one runs as root user on every host listed in 'Hosts.root':
dish -u root -E 'ssh $user@$host "perl -pi -e
\"s/^START_XNTPD=.*\$/START_XNTPD=\\\"yes\\\"/g;\"
/etc/rc.config"' -g Hosts.root
f) Freeze accounts of users on a termination list
using a script called FreezeUser.sh, all accounts of users found on 'Terminate.User.lst' get frozen today at 24:00 o'clock on both server groups as defined in files 'Hosts.1' and 'Hosts.2':
dish -E 'ssh root@$host "cat Terminate.User.lst | while read UN;
do echo \"su - admin -c \\\$HOME/bin/FreezeUser.sh \$UN\" | at
24:00 ; done"' -g Hosts.1 -g Hosts.2
g) Print out remote configuration file of an automounter
login as 'admin' user on host 192.168.0.1, switch to 'root', then cat the file '/etc/auto.net' and print out the date. the -a option makes the program ask you for the root-password on the remote host:
dish -u admin -a '' -E 'rsh -l $user $host su - root -c \"cat
/etc/auto.net\; date\"' 192.168.0.1
h) Install a package on Debian GNU/Linux hosts
after mounting a fileserver over samba, install from there a debian dish-package on all running servers β but skip hosts on maintenance. three different passwords are needed for authentication: one for login, next for su-root, and the last for mounting the fileserver:
dish -a0 -a1 -a2 -g Debian.up -r Debian.maint -e 'su - -c \"mount
-t smbfs //FILESERVER/Packages.Dir /mnt/smb ; dpkg -i
/mnt/smb/dish_1.19.1_all.deb\"'
i) Check for system load >2 using default 'hosts' and 'pass' config files
dish '(uptime |egrep \" (\[2-9\]|1\[0-9\])\\.\" && hostname)
|paste - -'
j) Query a MySQL database on remote host 10.0.0.1
dish -pp -c 'mysql -p -u $user -h' -e 'use mysql; show tables;
describe user;' -u root 10.0.0.1
k) Change password concurrently on all hosts/accounts
we're assuming the list of user accounts lives in file 'Accounts.lst', and an entry in that list looks like "user@hostname". after command execution, you'll be asked first for the login password (the old password), and then for the new password, which eventually has to be retyped correctly:
dish -p '' -n '' -e passwd -g Accounts.lst
or alternatively, processing concurrently and quietly on all hosts:
dish -pp -nn -f -Q -e passwd -g Accounts.lst
when you want to change password and use -nn, then the a1-password is implicitly set equal to the login password (the a0-password). noted. π
l) Change password from 'root' account (don't use the `-a0' option)
if you're going to change the root-password on 'remotehost', then try:
dish -nn -e passwd root@remotehost
the same as before, but login as user 'admin' (login password), then switch to 'root' (a1-password), and finally update the root-password:
dish -a1 -nn -e 'su -c passwd' admin@remotehost
changing the password for 'admin' on 'localhost', after logging in as 'root' via telnet, goes like:
dish -nn -c telnet -u root -e 'passwd admin' localhost
heads up bestie π for password change, when-p ""(or equivalently-a0or-pp) is not explicitly used, the assumption is made thatpasswdwill not ask for the old password β like in the case of a password change by 'root'. same thing is true if you can log into an account without typing a password, but thenpasswdprompts you to type the old one β this happens when you're using a ssh-key for login without password-authentication. for that scenario the correct choice of options is-p0 -aa -nn.
in case of properly prepared configuration files in $HOME/.dish, you can use dish as a distributed shell for a virtual cluster of hosts, and run it without specifying any program parameters β just issue a command, like for instance dish df -k / or dicp .profile :. lowkey iconic. π
and as a very last note: you should be aware that in case of authentication by password, dish's automated login process is based on the expectation that the login prompt sent to the terminal will include the case-insensitive regex-string Password: *$ (but see also -X). otherwise the authentication procedure will fail. fr fr. π
π BUGS AND KNOWN PROBLEMS π
if Tcl is compiled with thread support, the program hangs when executed in parallel mode (options -f or -F) β it seems to be a Tcl problem. (not me getting ghosted by my own processes π)
generally, at present Debian GNU/Linux (and other debian-based Linux distros as Ubuntu, Knoppix, etc.) pre-package Tcl with multi-thread support enabled. therefore, on such systems dish fails to process hosts in parallel. in this case you can download the debian source package of Tcl, remove the option --enable-threads in Β΄debian/rulesΒ΄, rebuild the package with dpkg-buildpackage -rfakeroot, and eventually install it. it could be a good idea to put the freshly installed package on hold. otherwise, you should recompile it on every tcl upgrade. π«
π’ REPORTING BUGS
report bugs to <gnu@mirendom.net>
Β©οΈ COPYRIGHT
Copyright Β© 2003-2013 Dimitar Ivanov
License: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
π SEE ALSO
expect(1), tcl(3), ssh(1), rsh(1), telnet(1)
and that's a wrap, bestie π this was dish 1.19.1, dated September 2013 β go forth and distribute commands across all your hosts, you absolute icon πβ¨
π¦ where this page comes from
- debian bookworm: dish 1.19.1-1.1
- debian trixie: dish 1.19.1-1.1
- ubuntu jammy: dish 1.19.1-1.1
- ubuntu noble: dish 1.19.1-1.1
- ubuntu resolute: dish 1.19.1-2 Β· GPL-3.0-or-later
- ubuntu stonking: dish 1.19.1-2 Β· GPL-3.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms