I know that Esc + . gives you the last argument of the last command.
But I'm interested in first argument of the last command.
Is there a key binding to do so?
On the same lines, is there a generic way of getting the nth argument from the last command?
I know that in a bash script, you can use $0, $1 etc., but these don't work on the commandline.
Also, what about iterating through the 0th argument of previous commands, like we can do with the last argument by continuously pressing Esc + .?
!$ gets the last element of the previous command line argument.
Just as M-. (meta-dot or esc-dot or alt-dot) is the readline function yank-last-arg, M-C-y (meta-control-y or esc-ctrl-y or ctrl-alt-y) is the readline function yank-nth-arg. Without specifying n, it yanks the first argument of the previous command.
To specify an argument, press Escape and a number or hold Alt and press a number. You can do Alt--to begin specifying a negative number then release Alt and press the digit (this will count from the end of the list of arguments.
Example:
Enter the following command
$ echo a b c d e f g
a b c d e f g
Now at the next prompt, type echo (with a following space), then
Press Alt-Ctrl-y and you'll now see:
$ echo a
without pressing Enter yet, do the following
Press Alt-3 Alt-Ctrl-y
Press Alt-- 2 Alt-Ctrl-y
Now you will see:
$ echo ace
By the way, you could have put the echo on the line by selecting argument 0:
Press Alt-0 Alt-Ctrl-y
Edit:
To answer the question you added to your original:
You can press Alt-0 then repeatedly press Alt-. to step through the previous commands (arg 0). Similarly Alt-- then repeating Alt-. would allow you to step through the previous next-to-last arguments.
If there is no appropriate argument on a particular line in history, the bell will be rung.
If there is a particular combination you use frequently, you can define a macro so one keystroke will perform it. This example will recall the second argument from previous commands by pressing Alt-Shift-Y. You could choose any available keystroke you prefer instead of this one. You can press it repeatedly to step through previous ones.
To try it out, enter the macro at a Bash prompt:
bind '"\eY": "\e2\e."'
To make it persistent, add this line to your ~/.inputrc file:
"\eY": "\e2\e."
Unfortunately, this doesn't seem to work for arg 0 or negative argument numbers.
To use the first argument, you can use !^ or !:1
Example:
$ echo a b c d e
a b c d e
$ echo !^
echo a
a
$ echo a b c d e
a b c d e
$ echo !:1
echo a
a
Since your question is about using any other arguments, here are some useful ones:
!^ first argument
!$ last argument
!* all arguments
!:2 second argument
!:2-3 second to third arguments
!:2-$ second to last arguments
!:2* second to last arguments
!:2- second to next to last arguments
!:0 the command
!! repeat the previous line
The first four forms are more often used. The form !:2- is somewhat counter-intuitive, as it doesn't include the last argument.
I liked #larsmans answer so much I had to learn more. Adding this
answer to help others find the man page section and know what to
google for:
$ man -P 'less -p ^HISTORY\ EXPANSION' bash
<...>
Word Designators
Word designators are used to select desired words from the event.
A : separates the event specification from the word designator.
It may be omitted if the word designator begins with a ^, $, *, -,
or %. Words are numbered from the beginning of the line, with the
first word being denoted by 0 (zero). Words are inserted into the
current line separated by single spaces.
0 (zero)
The zeroth word. For the shell, this is the command word.
n The nth word.
^ The first argument. That is, word 1.
$ The last argument.
% The word matched by the most recent ‘?string?’ search.
x-y A range of words; ‘-y’ abbreviates ‘0-y’.
* All of the words but the zeroth.
This is a synonym for ‘1-$’.
It is not an error to use * if there is just one word in
the event; the empty string is returned in that case.
x* Abbreviates x-$.
x- Abbreviates x-$ like x*, but omits the last word.
If a word designator is supplied without an event
specification, the previous command is used as the event.
Tested on Ubuntu 18.04
To insert previous arguments:
Alt+.: insert last argument from last command.
Alt+#+.: insert #nth last argument from last command.
Alt+- , # , Alt+., zsh: Alt+-+#+.: insert #nth first argument from last command.
In Linux you can repeat commands to go back in history
Example:
Last command is:
mv foo bar
Alt+0+.: insert first argument of last command = mv
Alt+2+.: insert last 2nd argument of last command = foo
up , Ctrl+w: last command without the last word = mv foo
General shortcuts
Ctrl+w: removes last word from cursor
Alt+d: removes next word from cursor
Ctrl+k: cuts everything after cursor
Ctrl+u, zsh: Alt+w: cuts everything before cursor
zsh: Ctrl+u: cuts the entire command (In bash you can combine Ctrl+u , Ctrl+k)
Ctrl+y: paste characters previously cut with Ctrl+u and Ctrl+k
Ctrl+_: undo last edit (very useful when exceeding Ctrl+w)
Ctrl+left: move to last word
Ctrl+right: move to next word
home or Ctrl+a: move to start of line
end or Ctrl+e: move to end of line
To iterate through the arguments in a previous command
only works in zsh
run or add this to your ~/.zshrc
autoload -Uz copy-earlier-word
zle -N copy-earlier-word
bindkey "^[:" copy-earlier-word
Now use Alt+. to go as back as you want, then use Alt+: to iterate through the arguments
Assuming last command is
echo 1 2 3 4 5
Alt+.: 5
Alt+.+:: 4
Alt+.+:+:: 3
Alt+.+:+:+:: 2
Alt+.+:+:+:+:: 1
Alt+.+:+:+:+:+:: echo
source: https://stackoverflow.com/a/34861762/3163120
To see all shortcuts available
bash: bind -lp
zsh: bindkey -L
I'm keeping this up-to-date here: https://github.com/madacol/knowledge/blob/master/bash-zsh_TerminalShorcuts.md
!^ may be the command for the first argument. i'm not sure if there is a way to get the nth.
You can also get arguments from any command in your history!
$ echo a b c d e f g
a b c d e f g
$ echo build/libs/jenkins-utils-all-0.1.jar
build/libs/jenkins-utils-all-0.1.jar
$ history | tail -5
601 echo build/libs/jenkins-utils-all-0.1.jar
602 history | tail -10
603 echo a b c d e f g
604 echo build/libs/jenkins-utils-all-0.1.jar
605 history | tail -5
$ echo !-3:4
echo d
d
$ echo !604:1
echo build/libs/jenkins-utils-all-0.1.jar
build/libs/jenkins-utils-all-0.1.jar
!^ will get you the first param, !$ will get you the last param, !:n will get you the nth element.
Basically it has a use in yanking previous (command's) arguments.
For instance, if the following command is issued:
echo Hello, world how are you today?
then, Hello, will be the first argument, and today? the sixth, that is the last one; meaning it can be referenced by typing:
Alt+6 followed by Ctrl-Alt-6
Ctrl is traditionally denoted as a hat character ^ prepended to keys names, and Alt as M- that is Meta prefix.
So the above shortcut can be redefined as ^My to yank.
Also, there is hats substitution shortcut in the command line:
echo Hello, world!
^Hello^Bye
Bye, world!
to substitute the previous command's first matched string, meaning:
Hello, world! Hello, people!
^Hello^Bye
would result in:
Bye, world! Hello, people!
leaving the second match (hello) unchanged.
Note: Do not leave space between hats, or the operation won't work.
The above is just a shortcut for:
!:s/Hello/Bye
event-level(*) substitution for the first found (matched) string in the previous command, while prefixing the first part with the g switch will apply to the whole line globally:
echo Hello, world! Hello, people!
!:gs/Hello/Bye
Bye, world! Bye, people!
as usually being done in other related commands such as sed, vi, and in regex (regular expression) - a standart way to search (match string).
No, you can't do !:sg/Hello/Bye or !:s/Hello/Bye/g here, that's the syntax!
! is for events; event might be understood as command output or operation done in the commands history.
That's what I understood by using it myself and trying things on my own from what I read from various sources including manual pages, blogs, and forums.
Hope it will shed some light into mysterious ways of bash, the Bourne-Again shell (a play on sh shell, which itself is called Bourne shell after its inventor's last name), what is default shell in many distributions including servers (server OS's).
The method described at the end of the accepted answer also works with the zeroth argument for me. I have these lines in my ~/.inputrc:
"\en": "\e0\e."
"\em": "\e1\e."
"\e,": "\e2\e."
\e2\e. has the advantage over \e2\e\C-y that it cycles through previous commands if it is pressed repeatedly instead of inserting the second argument of the previous command multiple times.
To insert the whole previous command, you can type !!\e^. \e^ is history-expand-line.
If you are on a mac you will tend to get extended characters with ctrl+letter. I have my right-of-space-bar-option key defined as meta in my terminal (iTerm2) set up. This means I use the key to navigate by word and pull parameters from previous commands.
For pasting 1th argument, press and hold down Alt key, and while it is down, hit the '1' key followed by the '.' key.
For pasting n-th argument, replace the '1' key above with the corresponding number key.
If this does not work, your terminal emulator may be catching the Alt key before it gets to shell. Some terminals (xfce4-terminal) allow turning off the "Alt-" shortcuts in the configuration file.
Credit to Jonas Eberle, I've fished this out from his comment to another answer here.
Related
We write two similar scripts: one for bash (linux) and one for batch (dos/windows).
Even if the specific code is different we would like to visually compare (diff) both scripts and have the similar parts of code aligned side by side.
We use explicit comments with the same text to achieve this. But the beginning of the comments is different in both scripting (REM or :: in windows) and (# in linux).
Therefore there is a wrong alignment:
linux
windows
# first step
REM first step
foo.sh
foo.bat
# second step
REM second step
bar.sh
bar.bat
Is there a way to use a common character or sequence of characters to make the comments equal?
Is the use of : #; safe for both systems/scripts?
linux
windows
: #; first step
: #; first step
foo.sh
foo.bat
: #; second step
: #; second step
bar.sh
bar.bat
Are there any unwanted side effects?
: in bash is not exactly a comment. It is a void command.
A little bit like pass in some languages.
It helps, for example, to fill empty slots, if needed
if condition
then
:
else
doSomething
fi
So, you may use, somehow, as a sort of comment. That would works both in bash and batch (well, I know nothing of batch. But since you said that :: is a comment there). But beware that it is not exactly a comment. So there are some differences
For example
#!/bin/bash
echo one ||
:: foo
echo two
echo un ||
# bar
echo deux
Displays one, two and un but not deux.
Because echo one || prints one and then execute the following command only if it fails (which it doesn't). Here the following command is :: foo. Which is not executed (you wouldn't know, since it does nothing, but it is not executed). And the echo two is a brand new unrelated command that is executed.
Whereas, on the other hand, echo un || likewise prints un, and doesn't execute the next command, since echo un did not fail. But the next command here is echo deux. Because # bar doesn't count, since it is a comment.
And that is only one of the many examples one could probably find to show that : is not a comment.
But, well, if you use it being aware of that, I suppose you could use it to insert void comments in your bash code that would also be void in batch.
Edit:
I won't edit for each new example that comes to mind. But that one is pretty important
echo un # deux
echo one : two
prints
un
one : two
: is a command. So, as other commands, like ls not all occurrence of it is treated as so (no more than echo ls list the directory constant. ls is just a string here)
So, you can't use it as a replacement for inline comments. Only for full lines comments.
I am running a for loop inside a while loop.
File passed as parameter has the contents:
peter
roger
casie
I am trying to create a path to test existence of files a,b,c,d,e
I am expecting the output to be
/peter/a
/peter/b
and so on.
Instead I am getting
/aeter
/beter
etc.
What do I need to understand here? Please find the code below -
CODE:
while read fileLine; do
x=$fileLine
for i in a b c d e
do
echo /$x/$i
done
done < $1
Apparently your input file uses the windows end-of-line format of \r\n. The read removes the \n but leaves the \r. When the string /$x/$i is printed, the "carriage" is returned to the beginning of the line at the end of the x string, printing the slash over top of the slash from x and printing the letter from i over the first letter of x.
You may be able to fix it by replacing your x=$fileLine line with
x=${fileLine%?}
which should remove the last character.
In bash sometimes I have very long commands where I need to edit some words. Right now I use End/Home to move end/start of the command, but what if I have to move say x characters in a line?
I need something like xb/xw of VI, but instead of words I need to move characters.
What about ditching emacs mode and switching to vi mode editing?
set -o vi
and you have all the power of vi-like command line editing, like 3l to go left three characters and 5B to go back 5 words. The Pos 1 key then becomes 0 and End becomes $.
In emacs mode, you can use Meta3Controlb to move back 3 characters, and Meta3Controlf to move forward 3 characters. For multi digit counts, you need to precede each digit with the Meta key (e.g., to move 10 characters back, Meta1Meta0Controlb).
Meta is usually the Alt key, but may be the Esc key instead (on Mac OS X, for instance).
(Yes, vi-command mode makes it easier.)
There is a command, universal-argument, that allows you to type all the digits at once, but it is unbound by default. Bind it with, say,
bind "\C-a":universal-argument
then typing Control-a will enter you into an "argument" mode, prefixing the current line (arg: 4), and allowing you to type digits to change the argument used by the next non-digit character you type. (See universal-argument in the bash man page for the full details.)
You could use the command as below
Command:
cp some_file1 some_file2 some_file3 /root/Desktop
After executing the command do the following
^some_file2^some_file4
and it will execute the command
cp some_file1 some_file4 some_file3 /root/Desktop ;
What happened is the some_file2 is replaced by some_file4 and the command is executed
I've got some source code like the following where I call a function in C:
void myFunction (
&((int) table[1, 0]),
&((int) table[2, 0]),
&((int) table[3, 0])
);
...the only problem is that the function has >300 parameters (it's an auto-generated wrapper for initialising and calling a whole module; it was given to me and I cannot change it). And as you can see: I began accessing the array with a 1 instead of a 0... Great times, modifying all the 300 parameters, i.e. decrasing 300 x the x-coordinate of the array, by hand.
The solution I am looking for is how I could force sed to to do the work for me ;)
EDIT: Please note that the syntax above for accessing a two-dimensional array in C is wrong anyway! Of course it should be [1][0]... (so don't just copy-and-paste ;))
Basically, the command I came up with, was the following:
sed -r 's/(.*)(table\[)([0-9]+)(,)(.*)/echo "\1\2$((\3-1))\4\5"/ge' inputfile.c > outputfile.c
Well, this does not look very intuitive on the first sight - and I was missing good explanations for nearly every example I found.
So I will try to give a detailed explanation on this:
sed
--> basic command
-r
--> most examples you find are using -e; however, the -r parameter (only works with GNU sed) enables extended regular expressions and brings support for the + in a regex. It basically means "one or more matches".
's/input/output/ge'
--> this is the basic replacement syntax. It basically means "replace 'input' by 'output'". The /g is a "global" flag, i.e. sed will replace all occurences and not only the first one. You can add an additional e to execute the result in the bash. This is what we want to do here to handle the calculation.
(.*)
--> this matches "everthing" from the last match to the next match
(table\[)
--> the \ is to escape the bracket. This part of the expression will match Strings like table[
([0-9]+)
--> this one matches numbers with at least one digit, however, it can also match higher numbers with more than only one digit.
(,)
--> this simply matches the comma ,
(.*)
--> and again: the rest of the line
And now the interesting part:
echo "\1\2$((\3-1))\4\5"
the echo is a bash command
the \n (you can use every value from \1 up to \9) is some kind of "variable" for the inputs: \1 will contain the first match, \2 the seconds match, ... --> this helps you to preserve parts of the input string
the $((1+1)) is a simple bash syntax to calculate the value of the term inside the double brackets (in the complete sed command above, the \3 will of course be automatically replaced by the 3rd match, i.e. the 1st part inside the brackets to access the table's cells)
please note that we use quotation marks around the echo content to also be able to process lines with characters like & which would otherwise not work
The already mentioned e of \ge at the end will trigger the execution of the result in the bash. E.g. the first two lines of the example source code in the question would produce the following bash statements:
echo "void myFunction ("
echo " &((int) table[$((1-1)), 0]),"
which is being executed and results in the following output:
void myFunction (
&((int) table[0, 0]),
...which is exatcly what I wanted :)
BTW:
text > output.c
is simple bash syntax to output text (or in this case the sed-processed source code) to a file called output.c.
Good links about this topic are:
sed basics
regular expressions basics
Ahh and one more thing: You can also use sed in the git-Bash on Windows - if you are "forced" to use Windows at work like me ;)
PS: In the meantime I could have easily done this by hand but using sed was a lot more fun ;)
Here's another way you could do it, using Perl:
perl -pe 's/(table\[)(\d+)(,)/$1.($2-1).$3/e' file.c
This uses the e modifier to execute an expression in the replacement. The capture groups are concatenated together but the middle group has 1 subtracted from its value.
This will output to standard output so you can check that it does what you want. When you're happy, you can add the -i switch to overwrite the original file.
This question concerns a bash script that is run in automator osx. I am using automator actions to get and filter a bunch of file references from the finder. Then I append to that list the name of the parent folder, also via an automator action. Automator then feeds these arguments to an action called "run shell script". I am not sure exactly how automator invokes the script but the argument list looks like this when echoed with: echo "$#"
/Volumes/G-Raid/Online/WAV_TEST/Testbok 50/01/01000 43-001.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50/02/02000 43-002.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50/03/03000 43-003.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50
In this case path to 3 files and a folder.
In the shell script I launch an application called ripcheckc* with the args passed from automator minus the last argument(the folder) in the list.
I use this to remove the last argument:
_args=( "$#" )
unset _args[${#_args[#]}-1]
And this is echo $_args:
/Volumes/G-Raid/Online/WAV_TEST/Testbok 50/01/01000 43-001.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50/02/02000 43-002.wav /Volumes/G-Raid/Online/WAV_TEST/Testbok 50/03/03000 43-003.wav
Same as before but without the folder.
Now, if I run ripcheckc with "$#" as argument it works (but fails later on because of that last path in the argument list) If I use ${_args[#]} the application will just abort silently. When I echo $# and _args the output looks identical except for the last argument.
My question is - what is the difference between $# and $_args that make the first valid input and the second not?
*The application is ripcheckc
I hope my question makes sense.
EDIT: Solved.
I have used this bash one-liner before
set -- "${#:1:$(($#-1))}"
It sets the argument list to the current argument list, less the last argument.
How it works:
$# is the number of arguments
$((...)) is an arithmetic expression, so $(($#-1)) is one less than the number of arguments.
${variable:position:count} is a substring expression: it extracts count characters from variable starting at position. In the special case where variable is #, which means the argument list, it extracts count arguments from the list beginning at position. Here, position is 1 for the first argument and count is one less than the number of arguments worked out previously.
set -- arg1...argn sets the argument list to the given arguments
So the end result is that the argument list is replaced with a new list, where the new list is the original list except for the last argument.
Assuming that you already have an array, you can say:
unset "array[${#array[#]}-1]"
For example, if your script contains:
array=( "$#" )
unset "array[${#array[#]}-1]" # Removes last element -- also see: help unset
for i in "${array[#]}"; do
echo "$i"
done
invoking it with: bash scriptname foo bar baz produces:
foo
bar
You can also get all but the last argument with
"${#:0:$#}"
which, honestly, is a little sketchy, since it seems to be (ab)using the fact that arguments are numbered starting with 1, not 0.
Update: This only works due to a bug (fixed in 4.1.2 at the latest) in handling $#. It works in version 3.2.
set -- "${#:1:$#-1}"
sets the parameter list to first up to penultimate, removing the last one