I've recently started using fish, and I needed to use a jar file for google's bundletool.
As such, I needed to set up an alias/function for bundletool, and I chose a function since it seems more "fishy".
My function is simple:
function bundletool
java -jar bundletool-all-1.12.1.jar $argv
end
The bundletool jar itself lives at ~/.local/bin, which is on my fish user path:
lase#laser-razer /m/c/U/matth [1]> echo $fish_user_paths
/home/lase/.local/bin /usr/local/go/bin /home/lase/.nvm /home/lase/.cargo/bin /home/lase/.cargo
In a regular shell, I can execute java -jar bundletool-all-1.12.1.jar, and the command runs as expected. However, in the function, fish doesn't seem to know about my fish_user_paths, and reports it cannot find the jar file.
To remedy this, I had to update my function to specify the full path:
function bundletool
java -jar ~/.local/bin/bundletool-all-1.12.1.jar $argv
end
This works, but I feel like I'm doing something incorrectly. Should I be setting up my paths or functions in a different fashion?
Your function will run in the ordinary environment.
It will run with the current $PATH [0] and, more importantly to you, the current working directory.
What happens is this:
java -jar bundletool-all-1.12.1.jar
Will tell java to run the jar found at bundletool-all-1.12.1.jar.
Notably, fish just hands java the string "bundletool-all-1.12.1.jar", and java will then not look at $PATH. It does not care about $PATH. It simply looks at "bundletool-all-1.12.1.jar", and tries to open a file by that name.
And it will find that file, if it is in the current directory.
And that's the reason this worked for you when you executed it interactively - because you happened to be in that directory
And then you tried it with the function, but you tried it from a different directory, and so it didn't work.
This works, but I feel like I'm doing something incorrectly. Should I be setting up my paths or functions in a different fashion?
No, giving the full path to the file instead of relying on the working directory is the right thing to do.
[0]: $fish_user_paths is just a variable you set, that fish will then take care to add to $PATH. $PATH is the actual variable that fish and other tools (including any command fish starts, if it wants to) will use to find commands.
Related
I am working on a program that I want to run a console command and save the output to a string. This program is installed by the user before running my program.
However, I have found I keep getting the error Cannot run program "XXXX" (in directory "C:\Users\accou\Downloads"): CreateProcess error=2, The system cannot find the file specified
For example, I have the program "Maui" that I have added to my system path:
Running in my Command Prompt:
C:\Users\accou\Downloads>maui
HELLO
C:\Users\accou\Downloads>
If I run the following code in Java I get the exception above
ProcessBuilder pb =
new ProcessBuilder("maui");
pb.directory(new File("C:\\Users\\accou\\Downloads"));
File log = new File("C:\\Users\\accou\\Downloads\\log.txt");
pb.redirectErrorStream(true);
pb.redirectOutput(ProcessBuilder.Redirect.appendTo(log));
Process p = pb.start();
assert pb.redirectInput() == ProcessBuilder.Redirect.PIPE;
assert pb.redirectOutput().file() == log;
assert p.getInputStream().read() == -1;
Exception:
Exception in thread "main" java.io.IOException: Cannot run program "maui" (in directory "C:\Users\accou\Downloads"): CreateProcess error=2, The system cannot find the file specified
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1142)
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1073)
at Application.main(Application.java:13)
Caused by: java.io.IOException: CreateProcess error=2, The system cannot find the file specified
at java.base/java.lang.ProcessImpl.create(Native Method)
at java.base/java.lang.ProcessImpl.<init>(ProcessImpl.java:483)
at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:158)
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1109)
... 2 more
I will get the same error if I try commands such as "dir" which I know 100% is available in windows.
However, if I can the command to the following it workes perfectly fine.
new ProcessBuilder("cmd.exe", "/c", "maui");
The following also works
new ProcessBuilder("maui.cmd");
Why is it that I cannot run "dir" or more importantly "maui" directly?
You're confusing ProcessBuilder with a shell. ProcessBuilder is not 'ask the dos box cmd shell to act as if we ran this statement', and when you type something in that black box, such as 'dir', that is not the same as 'ask the OS to execute this process'.
The shell does all sorts of intriguing transformations and interpretations of what you typed. That's cmd.exe (or, on other OSes, e.g. /usr/bin/bash), it is not the OS doing that, and java does not ship with a bash or cmd.exe baked in. It just asks the OS to do what you asked.
Thus, you can't use all these shellisms. Unfortunately, java tries to do some very basic shellisms, which just adds confusion. In particular:
The single-string argument variant will split on spaces to attempt to extract the executable to run + the arguments to pass.
On windows, *.txt and friends probably works. On other OSes it probably does not, as expanding a star into all matching files is a bashism. Best not use it.
built-ins, such as cd, pwd, test, dir, and others definitely do not work.
Relying on $PATH probably doesn't work, but this is one that java does sometimes try to apply. Still, best not rely on it.
Any fancy redirects and the like are all shellisms and do not work. You can't ask java to redirect to printer using foo.exe >PRN or /usr/bin/whatnot >/dev/printer. You can't write an if or a loop, or use %foo% or $foo or any other such things.
This leads to the following conclusion:
Do not ever use relative paths for anything ProcessBuilder related.
Do not ever try to squeeze the executable and the arguments into a single string; always use the multiple strings variant.
Don't rely on ANYTHING other than an absolute path, and absolute arguments.
If you need any shellisms of any sort, run cmd.exe /c C:\absolute\path\to\something.bat, or on posixy systems, /bin/bash -c /abs/path/to/shellscript, and strongly consider on windows to use System.getenv to get the installed location of windows, so you can absolute-path cmd to thatPath + "cmd.exe", so that you end up with "C:\\Windows\\cmd.exe" or equivalent (windows may not actually be installed there, hence, use env).
Unwieldy? Yeah. Don't use ProcessBuilder unless you know what you are doing.
In this case, if type maui on the command line, it's cmd.exe that figures out: Oh, hey, there is a maui.cmd in this dir on the path, surely they meant that. That's a shellism. Not baked into the OS itself, and java does not ship with cmd.exe built in, so that does not work. dir is not an executable but a built-in feature of cmd.exe. Same rule.
Java can do all these baked in things. There is no need to call dir - java can walk paths and give you all relevant info with e.g. the java.nio.file.Files API.
I am not sure this is clear to me and if is neat for my system. I'm aware of the ~/.zhrc file where I can store alias and paths, but today after installing node via brew I was asked to put this: export PATH="$HOME/.npm-packages/bin:$PATH" in my ~/.bash_profile file, which it doesn't exist, thus in my effort to keep my system clean I putted it in the former file but emacs complaint. Now, I removed it and putted it, after creating, in the ~/.bash_profile. Is that OK to keep both in the home directory?
You need to provide the exact wording of whatever error or warning message you
get from emacs to ensure accurate or better answers. However, I will make a
guess and assume the warning you are getting is from the exec-path package.
This package has a check, which you can disable, that looks to make sure you
have variables defined in the correct init file.
In general, most shells support two types of configuration files
Startup or Login init files
Interactive shell init files
The difference is how often or when the files are sourced (loaded). To
understand the difference, you really need to understand when a shell is run and
the relationship between each shell. I'll try to give a vary high level
explanation, but you really should read the manual page for the particular shell
you are using.
Think of your environment as a tree of shell processes. When you login to the
system, a login shell is created. This shell will be the parent of all the other
shells you create. Each time you run a command, it is executed in a new shell
(this isn't 100% accurate, but is accurate enough to explain the main
points). So when you open a terminal, it runs another shell which is a child of
your login shell. When you execute various commands, the system creates a new
shell and runs that command inside the shell. These are all children of your
parent login shell. Some shells only exist for a short period of time (as long
as it takes to execute the command), others may last for hours, days or possibly
weeks (such as the shell that emacs is running in).
The important point to keep in mind is that child shells inherit various
settings from the parent shell. The idea of the 'export' command you will see in
front of some variables is actually a command to the shell telling it to export
the variable to child shells. For example, if we have a line like
export PATH=/usr/local/bin:/usr/bin:/bin
what we are really doing is
PATH=/usr/local/bin:/usr/bin:/bin # set the variable
export PATH # make it available in child shells
We don't always want variables to be exported as some variables need to be reset
in the child shell itself. For example, the variable holding the prompt string.
It would not work to have this variable only defined in the login parent shell
if you want the prompt to have dynamic components, such as the current
directory, date or time. We want these types of variables to be defined in each
shell when it is created.
To handle this, shells have the two different init files. The login init files
are only sourced for the parent shell and are particularly useful for setting
variables that will be common to all child shells. the per-shell init files are
sourced for every new shell and are best used for setting things which need to
be updated or changed each time a shell is started. There are also other shell
configuration files which can be used for other special purposes, such as when
you log out or log off a system, or to just put alias definitions in etc.
Once upon a time, it made a big difference where you put your variables as there
was a performance hit when sourcing these init files. If the per-shell init file
was too large and consumed too many resources, the whole performance of your
environment could be affected. This is largely less of an issue these days due
to increased processing speeds. Unfortunately, because many people didn't
understand the role and relationships between the different shell configuration
files, there is lots of incorrect or misleading information out there regarding
where values should be set. People often advise setting variables in (for
example) bashrc when they should be set in the bash__profile=. The confusion is
partly caused by the fact you can add a variable to bashrc and it will work when
you test it (usually because your test involves forking a new child shell) and
putting it in your bash_profile will only work after the next login.
There are also some platform differences which make things a little less
clear. For example, under OSX, there is actually a special file in the /etc
directory where you should add additional path components (I'm not on a mac just
now, but it is something like /etc/paths or a per path component file in
/etc/path.d). This is done so that you have a global place to set paths which
will ensure desktop processes, such as the dock, which do not run as a child
process of your login shell, are able to be set.
As a general rule, most variables can go in the login profile, with the
exception of variables relating to the prompt or other variables which have a
dynamic content i.e. content which changes depending on time, directory
location or other tracking of interactive actions which are specific to a shell
instance.
Setting of the path (noting OS differences as described above) should go into
the profile or login configuration file. Under bash, this is .bash_profile and
under zsh, it is typically .zprofile. As bash has become the most common shell,
documentation etc often advises adding things to .bash_profile. If your running
zsh, then add the same information using .zprofile.
As you have said you don't have a.bash_profile, but you do have a zshrc file, I
am assuming you are running zsh rather than bash as your login shell. This being
the case, you need to add that path setting to .zprofile in your home
directory. The exec-path package is complaining because you added it to
zshrc/bashrc, which are not the correct place to set path variables. If your
running under OSX, you really need to add the path to the correct file in /etc
(you will need to check the OSX documentation as I cannot remember the precise
filename).
This question already has an answer here:
Wrong path set and now .bashrc throws errors [duplicate]
(1 answer)
Closed 6 years ago.
I put an invalid path into my bashrc and now my terminal doesn't work. I've deleted my bash_profile as well as my bashrc and I still can't get it to work.
Every command I use in terminal gives me the error:
sh: parse_git_branch: command not found
This was something I had in my bash_profile for git autocompletion. The problem is once I've deleted the files using /bin/rm <file>, I can't apply my changes with source ~/.bash_profile because it's giving me errors. I've looked at this question, and this question, but I still am having no luck.
How can I fix my command line after breaking things like this?
You can export a correct PATH in your current session using a command similar to :
export PATH=/usr/local/bin:/usr/bin:/bin
After using this, you should be able to source your bashrc.
You can always change your path right on the command line for the rest of the current shell session: PATH="/usr/local/bin:/usr/sbin:/usr/bin:/bin:/sbin:"
Exporting the PATH won't solve your problem. It will only export the value of PATH to child shells/processes. This only achieves the same thing as resetting/changing the PATH variable from the command-line (as shown above), because your scope is limited to the current shell process (and its children) while you are working within that shell environment (working from the command-line). If you open a new terminal (start a new login shell, which is a parent process), bash will still be trying to read from the resource files (.bash_profile and .bashrc), which no longer exist.
So, we need to be able to widen our scope and tell bash that we want to make changes for all future bashes (shell processes--new shells, shells within shells, etc.), and the way that is done is by writing those statements in the resource files.
Once you set your PATH there, every shell session will spawn with those resources (all the aliases, commands, variables, and functions stored in those files), loaded, regardless of whether you export it or not, since each shell process will always consult those files before it does anything else (so they effectively pre-emptively import what you are trying to export to them).
bash is like any other script or program. You took away all its inputs and pointers and shortcuts, and now its just been made pretty dumb, so you've got to be explicit with everything you do till you can teach it where to look for stuff (give it a PATH to search) and shorten your conversations with it.
If you don't have a backup of your .bash_profile, here's one that you can use:
# This loads in the configuration in .bashrc
# Put all configuration in there!
if [ -f ~/.bashrc ]; then
source ~/.bashrc
fi
And here's a .bashrc that can get you started:
export PATH="/bin:/sbin:/usr/local/bin:/usr/bin:/usr/sbin:"
I have this perl script that I need to distribute to my coworkers who want to run the script from anywhere in the unix environment. What can I do on my part to make running this PERL script easy for them? For example, they can just have the PERL script somewhere in their directory and run just typing
./xyz.pl ttt.conf
with no path declared (like /home/abc/bin/ddd/xyz.pl ttt.conf).
The way I used to do it is add a "bin" directory in your home directory, and add it to the $PATH variable.. then you can add any script you want to use to that directory.
I am no longer familiar with the exact syntax, but something like:
in .bashrc:
$PATH = ( $PATH , $HOME/bin )
Then place the script in /home/user/bin (assuming $HOME == /home/user). When you reload the shell, it will be usable like any normal command/program.
ETA: See robert's comment below on syntax. Also, to allow your co-workers to use a script of yours, you can simply use a hard-coded path, such as /home/patrick/bin.
Put the script in /usr/local/bin (or anywhere else in $PATH). Your sysadmin may have to help you.
The technique I use is:
#!/usr/bin/env perl
This is a common way of getting the command interpreter to find Perl without either (a) moving the file, or (b) declaring the explicit path for Perl in the shebang.
It's mentioned under portability at: http://en.wikipedia.org/wiki/Shebang_(Unix)
You all are kind of right... but that perl script can sit in your path till the cows come home... and it ain't gonna run... until you set the executable bit....
:bin localadmin$ ./perlextip
-bash: ./perlextip: Permission denied
:bin localadmin$ chmod +x perlextip
:bin localadmin$ ./perlextip
Exit 0! Yeehaw.
Also, it should be noted that it need not be IN your path.... You can just call it by the full path, preceeded with a period and a slash, to execute it..
:/ localadmin$ ./ServiceData/UNIX/bin/extip
Exit 0! Yeehaw.
You can also create an alias for such a command in your ~/.bash_profile, or the such, which will let you make a system-wide shortcut of sorts, and you can even throw in a sudo, or the like, if you were so inclined... Then just call that "extip" by name anywhere, you'll be prompted for a password and, all will be well in the world.
alias extip='sudo ./ServiceData/UNIX/bin/extip'
This has be stump. I wrote a shell program in C that allows the user to execute several commands. Based on my research so far, all the commands such as "ls" and "cat" are located in "/bin/".
The "wc" is not listed in this directory - "/bin". If I fire up a terminal, I can type "wc fileName" and it works. I ran "find . wc" from the "/" directory, and I still can't find the "wc" command.
Does anyone know where "wc" is hiding?
Try typing which wc into your shell...that should tell you where it is.
On my machine it is in /bin/.
However, if you just want the path resolution to be done on it's own, you can use the system() function (see man 3 system for more information). As you can read in the documentation, that's really the same as invoking the Bourne shell (or wherever the symlink for that points to) for the path resolution, so if you don't want that overhead, you will want to stick with whatever method you are currently using.
I tried whereis wc and I get it in /usr/bin/wc
If you don't want to worry about where individual utilities are, but you do want to avoid the overhead involved in calling system, then you should try the middle-level function execvp, or one of its friends (also listed on that page). Sadly, there is no execvpe.
You can try whence, which, or whereis to find any program in your exec path, depending on which shell you're using.
Utilities like wc are usually located in /binor /usr/bin, or in places like /usr/local/bin or /usr/site/bin.