Live data from Hacker News

Path Isn't Real on Linux

blog.danielh.cc

111–120 of 120 posts

Re: Path Isn't Real on Linux

#111

Earlier quoted context omitted.

Wtf. TIL about hash.

Using "hash" is arguably the best way to determine if a command is available in a BASH script e.g. hash java 2>/dev/null || printf "java command not available\n"

If you want to be compatible across all shells, use command -v. POSIX mandates it exists and has that returncode behaviour, whereas it doesn't mandate the hash, which or where command

    command -v java >/dev/null || echo 'no java command in path'
...and of course, if you're going to run the command anyway, and you know an invocation that does nothing and always exits with success, you can do that too. I like doing running "--version" or equivalent in CI systems, because it has the side effect of printing what actual versions were in use during the run.

    java -version || { echo >&2 'no java command in path'; exit 1; }
    git --version || { echo >&2 'no git command in path'; exit 1; }
    gcc --version || { echo >&2 'no gcc command in path'; exit 1; }

Re: Path Isn't Real on Linux

#112

Earlier quoted context omitted.

Username checks out. I think one of the most surprising things I learned about bash is that you can do this: touch ./-rf rm * And now you have rm -rf'd. :)

Indeed, always prefer ./* to * I often wish there was a convenient way of doing such an operation in the shell: if path start with "/", leave it, otherwise prepend "./"

Prepend for all paths on a command line? Or just for the executable?

For all paths it could be dangerous and should very probably not be done. But for executables it's less dangerous and can easily be done by putting '.' into $PATH.

Re: Path Isn't Real on Linux

#113

Earlier quoted context omitted.

Using "hash" is arguably the best way to determine if a command is available in a BASH script e.g. hash java 2>/dev/null || printf "java command not available\n"

If you want to be compatible across all shells, use command -v. POSIX mandates it exists and has that returncode behaviour, whereas it doesn't mandate the hash, which or where command command -v java >/dev/null || echo 'no java command in path' ...and of course, if you're going to run the command anyway, and you know an invocation that does nothing and always exits with success, you can do that too. I like doing runn…

Yeah, if you're targetting POSIX shells, then "command -v" may be more reliable.

If you're targetting BASH, then "hash" is a builtin so maybe slightly quicker (not that it's likely to be an issue) and it caches the location of "java" or whatever you're looking for, so possibly marginally quicker when you do want to run the command.

Whilst running "java -version" may be useful in some scripts (my scripts often put the output into a debug function so it only runs it when I set LOG_LEVEL to a suitable value, but it writes output to a file and STDERR), you run into an issue of "polluting" STDOUT which then means that you're not going to be using your script in a pipeline without some tinkering (ironically you're putting the failure message into STDERR when you probably don't care as the script is exiting and hopefully breaking the pipeline). Also, it can take some research to figure out what invocation to use for a specific command, whereas the "hash" version can just be used with little thought.

By the way, I don't believe that ">&2" is POSIX compliant, but that's trivial to fix.

Re: Path Isn't Real on Linux

#114

Earlier quoted context omitted.

If you want to be compatible across all shells, use command -v. POSIX mandates it exists and has that returncode behaviour, whereas it doesn't mandate the hash, which or where command command -v java >/dev/null || echo 'no java command in path' ...and of course, if you're going to run the command anyway, and you know an invocation that does nothing and always exits with success, you can do that too. I like doing runn…

Yeah, if you're targetting POSIX shells, then "command -v" may be more reliable. If you're targetting BASH, then "hash" is a builtin so maybe slightly quicker (not that it's likely to be an issue) and it caches the location of "java" or whatever you're looking for, so possibly marginally quicker when you do want to run the command. Whilst running "java -version" may be useful in some scripts (my scripts often put the…

As far as I know, its POSIX compliant?

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V...

    The redirection operator:

    [n]>&word

    shall duplicate one output file descriptor from another, or shall close one. If word evaluates to one or more digits, the file descriptor denoted by n, or standard output if n is not specified, shall be made to be a copy of the file descriptor denoted by word

Re: Path Isn't Real on Linux

#115

Earlier quoted context omitted.

Yeah, if you're targetting POSIX shells, then "command -v" may be more reliable. If you're targetting BASH, then "hash" is a builtin so maybe slightly quicker (not that it's likely to be an issue) and it caches the location of "java" or whatever you're looking for, so possibly marginally quicker when you do want to run the command. Whilst running "java -version" may be useful in some scripts (my scripts often put the…

As far as I know, its POSIX compliant? https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V... The redirection operator: [n]>&word shall duplicate one output file descriptor from another, or shall close one. If word evaluates to one or more digits, the file descriptor denoted by n, or standard output if n is not specified, shall be made to be a copy of the file descriptor denoted by word

Fair enough - I usually just target BASH and don't worry about POSIX compliance.

Re: Path Isn't Real on Linux

#116
post #25

Fun fact: if you've ever had bash (or another shell) complain that a file doesn't exist, even though it's on $PATH, check if it's been cached by `hash`. If the file is moved elsewhere on $PATH and bash has the old path cached, you will get an ENOENT. The entire cache can be invalidated with `hash -r`.

Unsure of in which situation, but I've had situations where a script didn't have the right shebang, and as such I had to resort to `alias --save hash='#'` to make sure the script worked.

Re: Path Isn't Real on Linux

#117

Earlier quoted context omitted.

Reading the code to things is perfectly fine, actually.

It's fine to understand what the code is doing in a shallow way. But this leaves out a lot of important information. Information that isn't immediately obvious just by reading code, that can help you avoid problems and understand the system in-depth, without years of trial and error. Which is why they wrote a manual. You can also both read the code and the manual, but the manual will give you much more knowledge in a…

In my experience this isn't necessarily true.

Re: Path Isn't Real on Linux

#118
post #20

Earlier quoted context omitted.

It seems too far to go to say that because a system library holds some implementation details that the responsibility doesn't lie with the program using them. There's all sorts of complex interdependent details that make those kind of boundary distinctions difficult in many operating systems.

On Linux the main boundary between user space and kernel is quite clear: the system call layer. It is stable and well documented. https://github.com/torvalds/linux/blob/master/Documentation/... System libraries like glibc are not part of the kernel, they are just components that can be replaced. I wrote an article about it: https://www.matheusmoreira.com/articles/linux-system-calls I even asked Greg Kroah-Hartman abo…

> You could write a static freestanding application and boot Linux directly into it.

> Nobody does stuff like this it's a lifetime of work. But it could be done.

1) Go binaries on Linux don't need libc (except for NSS, which is glibc-only idiocy).

2) Running a static libc-less binary as init is fun and easy! https://github.com/tv42/alone https://gokrazy.org/

Re: Path Isn't Real on Linux

#119
post #7

Why would strace cat be useful here? By the time cat runs, it was obviously already found. It is basic knowledge that PATH is used by a command interpreter to locate the pathname of binaries. This is true for Window's cmd.exe as well. I never heard of a system where locating files for execution was performed by a kernel.

In the [exec][1] family of POSIX functions, if the command path doesn't contain a slash, then it's looked up in the PATH. > If the file argument contains a slash character, the file argument shall be used as the pathname for this file. Otherwise, the path prefix for this file is obtained by a search of the directories passed as the environment variable PATH [...] [1]: https://pubs.opengroup.org/onlinepubs/009695399/f…

Those are just functions in a library, not seen by strace which is about syscalls.

Re: Path Isn't Real on Linux

#120

Earlier quoted context omitted.

On Linux the main boundary between user space and kernel is quite clear: the system call layer. It is stable and well documented. https://github.com/torvalds/linux/blob/master/Documentation/... System libraries like glibc are not part of the kernel, they are just components that can be replaced. I wrote an article about it: https://www.matheusmoreira.com/articles/linux-system-calls I even asked Greg Kroah-Hartman abo…

> You could write a static freestanding application and boot Linux directly into it. > Nobody does stuff like this it's a lifetime of work. But it could be done. 1) Go binaries on Linux don't need libc (except for NSS, which is glibc-only idiocy). 2) Running a static libc-less binary as init is fun and easy! https://github.com/tv42/alone https://gokrazy.org/

> Go binaries on Linux don't need libc (except for NSS, which is glibc-only idiocy).

They don't need NSS either, they just chose to depend on it because getting rid of if was too painful. Everyone's addicted to glibc and nobody enjoys going through the withdrawal symptoms.

> Running a static libc-less binary as init is fun and easy!

It's incredibly fun. I wrote a freestanding Linux Lisp interpreter and my long term goal is to boot Linux into it and bring up the entire system from within the REPL.

https://github.com/lone-lang/lone

I haven't tested it but I bet Linux can already boot into it just fine. I still need to do more work to make it able to bring up the system though. I've implemented endianness independent memory access functions, now I need a binary structure encoder and decoder. That will enable programs to do kernel I/O properly.

What's holding me back right now is continuations. Language still doesn't have flow control. I've been slowly converting my interpreter into an interruptible virtual machine with continuations that can integrate with primitives written in C. Debugging this stuff turned out to be a nightmare.

Post reply on HN