I use the command line heavily and over the years have migrated from
zsh as a daily driver shell. I usually use a slightly customized oh-my-zsh environment, but some systems are on prezto; the differences are not large.
The most productive plugins I've been using for zsh are
history-substring-search, and lately I've been using the very powerful
fzf plugins for pulling up history.
Now, I'm finding one of the biggest pain points that remain for me in the command line is command argument reordering. Quite often I try to run a command
command very/long/filesystem/path/to/argumentA another/filesystem/or/network/path/argumentB
and realize I've got the order backwards.
Another even more common situation is when we do any "manual deployment" workflow: First you compare the new stuff with the real stuff, e.g.
diff /opt/app/static/www/a.html /home/user/docs/dev/src/a.html cp /home/user/docs/dev/src/a.html /opt/app/static/www/a.html
Ok, ok, last example, no more I promise. Perfect real world example right here. Let's get cracking with some file listing with sweet human sizes:
find /pool/backups -type f -print0 | xargs -0 ls -lh > filelisting
I want to size sort and pick:
sort -rhk5 filelisting | fzf -m > /these/are/the_chosen
Nice, that works! Oh but I need the paths here:
cut -d ' ' -f 10 /these/are/the_chosen
The output is garbage, we encountered a setback with
ls -lh getting frisky with spaces. But we're ready to regroup: Join the contiguous spaces. Let's opt for
tr -s to squeeze space chars, no need for a regex here. Though,
tr requires stdin:
cat /these/are/the_chosen | tr -s ' ' | cut -d ' ' -f 9- > filelist
Here we're already feeling the pain with cutting and pasting already. My choices here are always between awkward alternatives: I can move the long path or i can move the cut command. With either move, I have to reach over for the mouse to copy it, or i have to type it again in the new spot. I can't win. With a command line, even navigating around is cumbersome, and extremely so without word hops hotkeys set up. I can't even use my mouse to jump around rapidly. So, this is the inefficiency that I want to abolish. I want to eliminate the friction of grabbing a shell object (such as a valid path string) and move it freely left and right while I prototype out my monster pipelines. If I had that feature I could spam that 5 times to shove the path to the left of the
cut & flags, then construct the rest of the pipeline organically.
I believe the lack of line editing power is what the issue is here. In the most trivial transposition example, I can create a trivial shell script that perhaps I'd call
cpto that inverts the arguments and punts the work to the
cp command. But I don't want to have to do that, and it would not help me for the third example.
I'd like to be able to reorder the arguments that I've entered using a simple key combination, like I can do for various types of lists if I'm in Vim with plugins like sideways.
Does such a plugin exist for zsh? Does such a plugin exist for any other shells?
If not, how difficult would it be to implement for zsh? I think that the zsh-syntax-highlighting plugin proves that it should be possible to tokenize arguments. Indeed the shell knows how to fetch individual arguments from history: https://stackoverflow.com/a/21439621/340947
The pain point is so severe and common that I'm liable to write a simple script to bind to a hotkey that grabs the last entry in history and swaps the last 2 args for me, and runs that. But that would not be as ideal as having a line editor operation so that the swap can be done interactively rather than committing to run the command.
Perhaps an improvement on that could be injecting
!:0 !:2 !:1 (which zsh nicely auto expands for me) but there are plenty of problems with this also: (1) it won't work without already having attempted to run the wrong command. More than half the time I want to swap args after catching myself after having typed an incorrect command, and (2) often there are flags that were used which that snippet would fail to account for.
I've also seen the approach shown here which is fine but remains tremendously unsatisfying as the keystrokes need to be repeated a lot for long paths, and the Ctrl+Y behavior doesn't even recall the full stack of things deleted with Ctrl+W. It's basically useless.
For completeness' sake, the tactic taken now is to use whatever suitable key combo to delete words to erase the shorter of the commands to reorder, reposition the cursor, use the mouse to copy the deleted argument from terminal output, and paste it back in. Ordinary folk don't bat an eye at this but it makes me die a little every time I do it because I cannot stop thinking about how easy it would be for the computer to do this task for me, and the injustice that I feel having to reach my hand over to the mouse.