Fish shell functions show $argv in job control, not file - fish

I'm trying to switch to fish shell but I've run into one sticking point. If I alias vi to vim and edit a file, I can't see which file I'm editing in job control.
polis#josh1 ~> function testvi
vim $argv
end
polis#josh1 ~> functions testvi
function testvi
vim $argv
end
polis#josh1 ~> testvi foobar
polis#josh1 ~> jobs
Job Group CPU State Command
2 26087 0% stopped vim $argv
How do I make it so the output is:
2 26087 0% stopped vim foobar

This is a situation where a command abbreviation is preferable to an alias (i.e., a function):
abbr -a vi vim
Now when you type "vi" and press space it will be magically replaced by "vim". You can do more complicated expansions. For example I use these abbreviations quite a bit:
abbr -a gca git commit --amend
abbr -a gcm git checkout master
The advantages of the abbreviation is they are much simpler than a function and the expansion shows up in your shell history which I find more useful than an aliased function name.

The idea is we want to expand $argv first into a new command, and then execute that. We can do that with eval.
However, this has the wrinkle that any special characters in the new command will be interpreted, so we need to escape them first.
The overall function is:
function testvi
eval "vim "(string escape $argv)
end
This uses the new string builtin, which is in the just-released fish 2.3.0.
Illustration of the result, here editing a file 'foobar $baz' to show that escaping works:
> testvi 'foobar $baz'
> jobs
Job Group State Command
5 60249 stopped vim 'foobar $baz'

Related

Can you `split` fish shell variables as cmd line args

Is it possible to have fishshell split variables that are in cmd line arguments?
Assume I have a variable $args set like so:
$ set args "-a args"
Now, given this python program (test.py):
import sys
print(sys.argv)
If I run the above in fishshell I get this output:
$ python test.py $args
['test.py', '-a args']
Notice that the arguments are passed as one argument. When I do the equivalent in bash I get this output:
$ python test.py $args
['test.py', '-a', 'params']
Is there someway to make fish behave like bash?
You do not want fish to behave like bash (technically any POSIX compatible shell) with respect to variable expansion. The POSIX behavior is the source of endless problems and is why you need to put double-quotes around almost everything. In fact, most experienced people will tell you to add IFS=$'\n' at the top of your scripts to stop that auto-splitting from happening.
One answer is to use fish's "every var is a list" feature: set args "-a" "args" (the quotes are just for clarity and aren't needed in this example). Each element of the list becomes a separate argument to the command. This will do the right thing even if the args value contains whitespace. The other answer is to explicitly split the string on whitespace using command substitution: a_cmd (string split ' ' $args). This will not do the right thing (in fish or bash) if the args value contains whitespace.
I found a little hack with fish commandline tokenization:
function posix_expand_str --description "Expand a string the POSIX way."
set __posix_expand_str__oldline (commandline)
commandline $argv
commandline -o
commandline $__posix_expand_str__oldline
set -e __posix_expand_str__oldline
end
All strings seem like they were concatenated during testing.
When you realize this answered your question, please accept. It only POSIXes when you ask it to, and does not break strings.
Test results:
> posix_expand_str "hello world"
hello
world
> posix_expand_str "hello 'posix haters' world"
hello
posix haters
world
> posix_expand_str "hello" 'high rep "stackoverflow staff"' "world"
hello
high
rep
stackoverflow staff
world

Syntax error in script

My script try execute mvn clean install in all projects but before it tried switch to dev branch and pull it.
successString="[INFO] BUILD SUCCESS";
file="mvnoutput";
red=$'\e[1;31m';
grn=$'\e[1;32m';
end=$'\e[0m';
function checkResult
if grep -Fxq "$successString" $file
then
echo -en "${grn}[${1}]Build ok${end}";
else
echo "${red}[${1}]Error in mvn clean install${end}";
exit 1;
fi;
end
function pullAndSwitchDevBranch
git checkout dev;
git pull origin dev;
end
cd api-pay-commons/;
pullAndSwitchDevBranch;
touch mvnoutput;
mvn clean install -U > mvnoutput;
checkResult PAY;
Why I received this error?
line 17: end: command not found ./script.sh: line 20: syntax
error near unexpected token git' ./script.sh: line 20: git checkout dev;'
You have used the "fish" tag, so I'm assuming you are running this with the fish shell.
If so: This is not a valid fish script. Fish is explicitly not POSIX-compatible, so you might want to read up on the syntax. (If not, then please correct your tag)
red=$'\e[1;31m';
Fish does not use var=value to set a variable. It uses set var value.
Fish also does not have the $'' style of quoting. Instead, backslash-escapes are interpreted outside of quotes.
So this would be set red \e"[1;31m". Alternatively, fish offers the set_color builtin, which prints the escape sequence for a given color. So this could be set red (set_color red) (or you could call set_color later).
then
Fish does not use if condition; then dosomething; fi. It's if condition; dosomething; end.
echo -en "${grn}[${1}]Build ok${end}";
Fish does not use "${var}". It also does not call the function arguments $1 et al.
This would be something like echo -ens "$grn" "[$argv[1]]" "Build ok" "$end".
exit 1
Fish currently does not allow exiting from functions. You'll have to return 1 and check the result from the outside.
Additionally, you're using semicolons all over the place, which is not typical style in any shell I'm aware of.
In case you are not using fish, but some POSIX-compatible shell (like bash), this is also not valid. The main (and possibly only) issue is that you are using function something; dosomething; end. In bash, that would be
function something () {
#dostuff
}
And in pure POSIX, that would be
something () {
#dostuff
}

Need to convert ksh command line to sh

I'm trying to do a simple operation in ksh that I need to repeat in sh (Bourne shell)
All I want to do is append the contents of the first line of hte pay_period.txt file to the end of the new file name. This works great in ksh, but does not work in bourne. The program I'm using defaults to sh and I can't change that. Also I can't have actual shell scipts in the directories. So I have to issue commands.
How can I make the equivalent command below work in bourne
mv HEPAY.txt HE_PAY"$(/usr/bin/head -1 pay_period.txt)."txt
The results of $(/usr/bin/head -1 pay_period.txt) is 20140101.
If you are really talking about a real Bournce shell then you need to use backticks for command substitution ($() is POSIX and portable among "modern", POSIX-compliant shells but won't work in old, legacy shells), e.g.
mv HEPAY.txt HE_PAY`/usr/bin/head -1 pay_period.txt`.txt
Other than that I see no reason why this should not work.
PS: Note that head -1 isn't POSIX-compliant either (head -n 1 is).

Cygwin shortcut for command history

How can I search the command history in cygwin?
I don't want to keep pressing the arrow keys to execute a command from console command history.
If you are using the default editing mode, do ctrl+R to search back through your history.
If you have done set -o vi to use vi editing mode, then it is esc-/
The history command is the way to go. I use
h ()
{
history | cut -f 2- | sort -u | grep -P --color=auto -e "$*"
}
so that I can type something like h git.*MyProgram, h ^tar -c, h svn:ignore, etc to pull up a sorted list of past commands matching a regex.
You might also want to add the following lines to ~/.inputrc:
# Ctrl+Up/Down for searching command history
"\e[1;5A": history-search-backward
"\e[1;5B": history-search-forward
With these in place, you can type a partial command prefix (such as gi or sql) then use Ctrl+Up to scroll back through the list of just your command history entries that match that prefix (such as git clone https://code.google.com/p/double-conversion/ and sqlite3 .svn/wc.db .tables). This can be a lot faster than searching and then cutting and pasting if you want to edit or re-execute a command that was fairly recent.
I use the history command in combination with grep, e.g. history | grep vi shows all commands where vi was used.
Checkout the "Gnu Bash Manual" (man bash) for the command "fc". E.g.fc -l -80 would list the last 80 commands, while other options let you search with RegEx...
Do
vi ~/.inputrc
Add
For arrow up/down bindings:
"\e[A": history-search-backward
"\e[B": history-search-forward
Or for page up/down bindings:
"\e[5~": history-search-backward
"\e[6~": history-search-forward
Close and open cygwin.
Voila.
I think one of the easiest way is to pipeline it with less and press search character ("/") and then type the command you wanna find.
history | less
and then
/text to find
to find the desired command
Another way
is to append the stdout form history command to a file: history > /cygdrive/path/file.txt
and then search in the document.

cygwin sed substitution against commands in history

I couldn't find an answer for this exact problem, so I'll ask it.
I'm working in Cygwin and want to reference previous commands using !n notation, e.g., if command 5 was which ls, then !5 runs the same command.
The problem is when trying to do substitution, so running:
!5:s/which \([a-z]\)/\1/
should just run ls, or whatever the argument was for which for command number 5.
I've tried several ways of doing this kind of substitution and get the same error:
bash: :s/which \([a-z]*\)/\1/: substitution failed
As far as I can tell the s/old/new/ history substitution syntax only does simple string substitution; it does not support full regexes. Here's what man bash has to say:
s/old/new/
Substitute new for the first occurrence of old in the event line. Any delimiter can be used in place of /. The final delimiter is optional if it is the last character of the event line. The delimiter may be quoted in old and new with a single backslash. If & appears in new, it is replaced by old. A single backslash will quote the &. If old is null, it is set to the last old substituted, or, if no previous history substitutions took place, the last string in a !?string[?] search.
Never fear, though. There are in fact easier ways to accomplish what you are trying to do:
!$ evaluates to the last argument of the previous command:
# ls /etc/passwd
/etc/passwd
# vim !$
vim /etc/passwd
!5:$ evaluates to the last argument of command #5:
# history
...
5: which ls
...
# !5:$
ls
You can also use Alt+. to perform an immediate substitution equivalent to !$. Alt+. is one of the best bash tricks I know.
This worked for me using Bash in Cygwin (note that my which ls command was number 501 in my history list; not 5 like yours):
$(!501 | sed 's/which \([a-z]\)/\1/')
You could also do it this way (which is shorter/cleaner):
$(!501 | sed 's/which //')