Set PATH for one command in fish - fish

In bash in order to update PATH just for one command I can do:
env PATH=/my/path:$PATH mycommand.sh
What is the equivalent syntax for fish? I have tried many variations with spaces or colons, and none work.

In fish, $PATH is a list not a colon-separated string. You need to convert it to the expected format first:
env PATH=(string join ":" /my/path $PATH) mycommand.sh

Related

How to convert export line from bash to fish?

I have a bash profile as to add a ':' if LIB exist, skip the ':' when LIB not defined.
export LIB="{LIB:-}${LIB:+:}/example/lib"
If run in fish, it will show
${ is not a valid variable in fish.
Any trick to convert this line to fish?
In short: In this simple case, try
set -gx LIB (string join : -- $LIB /example/lib)
This will construct a string with string join - if $LIB was set, it'll add it and a : before "/example/lib", if it wasn't it won't.
The underlying thing here is that fish doesn't have these variable expansion shortcuts like ${foo:+-/%#} and such.
The intention here is to do something similar to what you would do with $PATH - "add :/foo if $PATH is set, or set it to /foo otherwise".
For $PATH, because it is a list in fish (as it is a PATH variable - these are automatically joined/split with ":"), you would simply do
set -gx PATH $PATH /foo
You could define your variable $LIB as a path variable like
set -gx --path LIB $LIB /example/lib
and fish would do the same for it. Now inside fish it would be accessible as a list so you could use $LIB[1] to get the first element, and outside of fish it would be available in the colon-delimited form so applications know what to do with it.
But if you don't want to do that, see the string join method above.

PowerShell command only works without spaces between arguments

I try certain source codes using PowerShell to extract an password protected archive using 7zip:
This command doesn' work (7zip is an alias for $7zipPath):
& 7zip x "$zipFile" -o "$output" -p $zipFilePassword
I get the this error:
Command Line Error:
Too short switch:
But when I remove the spaces between the variables -o and -p, the archive can be extracted. This behaviour confuses me with other command line tools like git etc.? Why is it so?
The behavior is specific to 7-Zip (7z.exe) and applies to whatever program (shell) you invoke it from:
Deviating from widely used conventions observed by CLIs such as git, 7z requires that even switches (options) that have mandatory arguments, such as -o and -p, have the argument directly attached to the switch name - no spaces are allowed:
& 7zip x $zipFile -o"$output" -p"$zipFilePassword"
Note that you normally need not enclose variable references in PowerShell in "..." (note how $zipFile isn't), even if they contain spaces. However, in order to attach them directly to switch names, you do.
Alternatively, you could enclose the entire token - switch name and argument - in double quotes:
& 7zip x $zipFile "-o$output" "-p$zipFilePassword"

tcl exec to open a program with agruments

I want to open a text file in notepad++ in a particular line number. If I do this in cmdline the command should be:
start notepad++ "F:\Path\test.txt" -n100
And it is working fine from command line. Now I have to do this from tcl. But I can't make this command work with exec. When I try to execute this:
exec "start notepad++ \"F:\Path\test.txt\" -n100"
I am getting this error:
couldn't execute "start notepad++ "F:\Path\test.txt" -n100": no such file or directory.
What am I missing. Please guide.
Similar to this question:
exec {*}[auto_execok start] notepad++ F:/Path/test.txt -n10
First, you need to supply each argument of the command as separate values, instead of a single string/list. Next, to mimic the start command, you would need to use {*}[auto_execok start].
I also used forward slashes instead of backslashes, since you would get a first level substitution and get F:Path est.txt.
EDIT: It escaped me that you could keep the backslashes if you used braces to prevent substitution:
exec {*}[auto_execok start] notepad++ {F:\Path\test.txt} -n10
You can simply surround the entire exec statement in curly braces. Like this:
catch {exec start notepad++.exe f:\Path\test.txt -n10}
I haven't found a perfect solution to this yet. All my execs seem to be different from each other. On windows there are various issues.
Preserving double quotes around filename (or other) arguments.
e.g. in tasklist /fi "pid eq 2060" /nh the quotes are required.
Preserving spaces in filename arguments.
Preserving backslash characters in filename arguments.
[Internally, Windows doesn't care whether pathnames have / or \, but some programs will parse the filename arguments and expect the backslash character].
The following will handle the backslashes and preserve spaces, but will not handle double-quoted arguments. This method is easy to use. You can build up the command line using list and lappend.
set cmd [list notepad]
set fn "C:\\test 1.txt"
lappend cmd $fn
exec {*}$cmd
Using a string variable rather than a list allows preservation of quoted arguments:
set cmd [auto_execok start]
append cmd " notepad"
append cmd " \"C:\\test 1.txt\""
exec {*}$cmd
Note that if you need to supply the full path to the command to be executed, it often needs to be quoted also due to spaces in the pathname:
set cmd "\"C:\\Program Files\\mystuff\\my stuff.exe\" "

How can I ensure my autocompleted spaces are fed into my function properly?

I'm using zsh, and am trying to write a function to operate on a URL and a pathname:
function my-function
{
somecommand --url $1 $(readlink -f $2)
}
(to complicate things somewhat, the function actually uses sh syntax, as it is sourced from my ~/.zshrc using a trick like this). The readlink is there to expand symlinks and ensure directories such as . are evaluated correctly (the directory name is stored for later use by somecommand).
When I type a command from the command-line like this:
my-function http://example.org/example /tmp/myexampledirectory
... it works fine, even if I autocomplete the directory name. However, if the directory name contains spaces, zsh completes it like this:
my-function http://example.org/example /tmp/My\ Example\ Directory
For most "normal" commands (cp, mv, etc.) that never seems to cause a problem. However, in my case, somecommand sees $2 as only being /tmp/My - presumably the rest is seen as another argument.
How can I avoid this situation? I would prefer not to alter the standard zsh autocompletion, but rather find a way for my function to handle this.
The zsh completion system works very well here, and the solution is very simple, just put double-quotes around the readlink argument in the script:
somecommand --url $1 $(readlink -f "$2")
The point is that without quotes readlink removes backslashes which escape whitespaces. Compare three results:
1. Without backslashes and quotes readlink -f assumes that there are three different files/directories (with default path in current directory) and produces
$ readlink -f /tmp/My Example Directory
/tmp/My
/home/jimmij/Example
/home/jimmij/Directory
2. With escaping backslashes but without quotes readlink -f understands that there is only one directory, but removes backslashes from output, so that somecommand takes three separate arguments
$ readlink -f /tmp/My\ Example\ Directory
/tmp/My Example Directory
3. With backslashes and with double-quotes readlink -f gives the output with backslashes what is (most probably) expected by somecommand
$ readlink -f "/tmp/My\ Example\ Directory"
/tmp/My\ Example\ Directory
BTW, as a rule of thumb: if there are any problems with whitespaces in the shell-like scripts (bash, zsh, whatever) the first thing to play with is different quotation marks around variables.

How to force bash/zsh to evaluate parameter as multiple arguments when applied to a command

I am trying to run a program like this:
$CMD $ARGS
where $ARGS is a set of arguments with spaces. However, zsh appears to be handing off the contents of $ARGS as a single argument to the executable. Here is a specific example:
$export ARGS="localhost -p22"
$ssh $ARGS
ssh: Could not resolve hostname localhost -p22: Name or service not known
Is there a bash or zsh flag that controls this behavior?
Note that when I put this type of command in a $!/bin/sh script, it performs as expected.
Thanks,
SetJmp
In zsh it's easy:
Use cmd ${=ARGS} to split $ARGS into separate arguments by word (split on whitespace).
Use cmd ${(f)ARGS} to split $ARGS into separate arguments by line (split on newlines but not spaces/tabs).
As others have mentioned, setting SH_WORD_SPLIT makes word splitting happen by default, but before you do that in zsh, cf. http://zsh.sourceforge.net/FAQ/zshfaq03.html for an explanation as to why whitespace splitting is not enabled by default.
If you want to have a string variable (there are also arrays) be split into words before passing to a command, use $=VAR. There is also an option (shwordsplit if I am not mistaking) that will make any command $VAR act like command $=VAR, but I suggest not to set it: I find it very inconvenient to type things like command "$VAR" (zsh: command $VAR) and command "${ARRAY[#]}" (zsh: command $ARRAY).
It will work if you use eval $CMD $ARGS.