3

I have this alias to format $PATH into one line per path:

alias path='sed ''s/:/\\n/g'' <<< "$PATH"'

I tried to specify the env var to list, like path $MANPATH but still with a default to $PATH:

alias path='sed ''s/:/\\n/g'' <<< "${@:-PATH}"'

But for path I get "PATH" and for path MANPATH I get:

sed: can't read MANPATH: No such file or directory

I also tried using a function so I could check for a missing argument and replace it with $PATH if no arguments were given.

7
  • 2
    Regarding not for >pathXL $PATH -- how to remove leading '$' if it is there? - you can't remove the $ from $PATH inside pathXL as $PATH would be expanded by your shell before pathXL is called. Anyway, make sure to just accept an answer to the question you asked, don't add more questions on top of it as then it becomes a chameleon question which is strongly discouraged, Commented Oct 22 at 14:36
  • consider updating the question with a list of examples ... show 2 different 'PATH' variable definitions, show the different ways you wish to call the alias (or function), and the expected results Commented Oct 22 at 14:56
  • 3
    The answer from @Renaud completely works for the question you originally asked. You should simply accept it and then ask a new question to address the additional requirements you have now added. If my response about expanding $ is "off the mark" then you'll need to clarify what you meant by not for >pathXL $PATH -- how to remove leading '$' if it is there? (in your new question, not here). Commented Oct 22 at 16:36
  • 1
    @vulcan_ Do not include answers you receive into the question please (you can reply on them using comments instead). How to Ask. How to Answer. Commented Oct 22 at 16:52
  • 2
    @vulcan_ Also, do not edit questions in ways that invalidate existing answers please. Commented Oct 22 at 17:20

5 Answers 5

4

Don't use an alias, use a function:

path() { local -n tmp="${1:-PATH}"; printf '%s\n' "${tmp//:/$'\n'}"; }

See the nameref attribute in bash manual to understand local -n. Note that newline and : characters in the variable's value are indistinguishable in the output. Example:

$ FOO='a:b:c'
$ path FOO
a
b
c
$ FOO=$'a\nb:c'
$ path FOO
a
b
c

Edit: use parameter substitution instead of IFS, as suggested in comments.

Sign up to request clarification or add additional context in comments.

1 Comment

2

Assumptions (from OP's SUMMARY OF ANSWERS edits):

  • if the alias (or function) is ...
  • a) called with no input then print the contents of the current PATH variable
  • b) called with the name of a variable then print the contents of said variable
  • c) called with a colon-delimited directory list then print said directory list

Additional assumptions:

  • we do not have to worry about directory names containing a colon
  • we need to address directory names containing white space (though we won't be able to distinguish between linefeeds that are part of the directory name vs linefeeds that are inserted to replace colons)
  • if providing a list of directories there will be at least one / in the list
  • if a list of variables is provide we will only process the first variable

One idea using a bash function and a nameref:

path() {

    case "$@" in
        */*) pathlist="$@"          ;;     # input is a list of colon-delimited directories
         "") pathlist="$PATH"       ;;     # no input => default to contents of PATH variable
          *) declare -n _path="$1"         # input is the name of a variable; associate with nameref '_path' and then ...
             pathlist="$_path"      ;;     # obtain contents via '$_path'
    esac

    tr ":" $'\n' <<< "${pathlist}"         # convert colons to linefeeds
}

NOTE: bash v 4.3+ required for nameref support

Taking for a test drive:

Variables we'll be testing:

declare -x PATH="/home/username/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -- pathA="/parent dir:/other dir:/usr/bin"

Call with variable name:

$ path PATH
/home/username/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin

$ path pathA
/parent dir
/other dir
/usr/bin

$ path pathA PATH             # only process first variable
/parent dir
/other dir
/usr/bin

Call with a colon-delimited directory list:

$ path "$pathA"
/parent dir
/other dir
/usr/bin

$ path "some_random/dirs:/var/log/apt:/another custom/dir"
some_random/dirs
/var/log/apt
/another custom/dir

$ path a b/dir:d e f/g h:/usr/bin
a b/dir
d e f/g h
/usr/bin

Call with no args:

$ path
/home/username/bin
/usr/local/sbin
/usr/local/bin
/usr/sbin
/usr/bin
/sbin
/bin

Comments

1

The problem is that aliases don't handle arguments properly, and ${@:-PATH} gives you the literal string "PATH" instead of the variable's value. Use a function with indirect expansion instead:

path() {
    local var="${1:-PATH}"
    sed 's/:/\n/g' <<< "${!var}"
}

The ${!var} syntax performs indirect expansion - it uses the value of var as a variable name and expands it.

Usage:

  • path → shows $PATH
  • path MANPATH → shows $MANPATH

With error handling:

path() {
    local var="${1:-PATH}"
    [[ -z "${!var+x}" ]] && { echo "Error: \$$var not set" >&2; return 1; }
    sed 's/:/\n/g' <<< "${!var}"
}

Add to your ~/.bashrc to make it permanent.

Comments

1

As mentioned several times, an alias probably isn't what you want - use a function.

Just for completeness, though - the problem with your alias is that you are thinking of it as having "arguments". An alias is just a string reparse.

alias path='sed ''s/:/\\n/g'' <<< "$PATH"' becomes
sed s/:/\\n/g <<< "$PATH" when you enter path on the CLI, so
path $FOO becomes
sed s/:/\\n/g <<< "$PATH" $FOO

The I/O redirection still happens, but sed isn't seeing that in its command list.
What it sees is that the command is now
sed s/:/\\n/g $FOO
which tries to open the value of $FOO as a file. If FOO=a:b:c it's looking for a file named a:b:c, and ignoring its stdin entirely.

$: echo $FOO $BAR
a:b:c 1:2:3

$: alias p
alias p='sed s/:/\\n/g <<< "$FOO"'

$: cat $FOO
a:b:c

$: cat $BAR
1:2:3

$: p # reads stdin from the *value* of $FOO
a
b
c

$: p $FOO # reads FILE input from file named $FOO
a
b
c

$: p $BAR # reads file input ONLY from file named $BAR, ignores stdin from $FOO
1
2
3

$: rm $FOO $BAR

$: p # Still reads stdin from value of $FOO
a
b
c

$: p $FOO
sed: can't read a:b:c: No such file or directory

$: p $BAR
sed: can't read 1:2:3: No such file or directory

alias doesn't take arguments, it only sets a string value to be reparsed on the CLI.
Since it makes several passes you can do some interesting things, but if you don't understand what's happening it can cause you grief.

You could define it this way -

$: alias p='sed s/:/\\n/g <<< '

$: p $FOO
a
b
c

$: p $BAR
1
2
3

but I do NOT recommend it. It's fragile.

Use a function, it's a lot more structured and has useful features like arguments.

Consider what might go wrong and include some error handling.

Comments

-1

folks .. thanks for you input ..

in the meantime some anonymous person with enough permissions to be able to edit my answer rewrote my update to capitalize the sentences and wipe out my chatty style of writing. You dont like what i write? or the way i write it .. leave the page, or at worst make a comment.
your suggestions led me to an answer in two parts:

pathToUpper() { printf '%s' ${1:-path} | tr [:lower:] [:upper:] ; }
pathlist() { local -n tmp=`pathToUpper $1`; echo $tmp | tr : \\n; }

# and then come the aliases
alias path='pathlist path'

alias llpath='pathlist ld_library_path'
alias libpath=llpath
alias pathlib=llpath
alias manpath='pathlist manpath'

i wanted to be able to type "path" to get $PATH listed out, one file path per line. I also wanted to be able to do the same for $MANPATH .. and $LD_LIBRARY_PATH.

in a comment @jhnc let me know that i could force upper case using ˆˆ so i am using that in my solution now

So I append the resulting upper case env var name to a '$' and get the result evaluated into the needed list. That is what the pathlist function is doing in these steps:

  1. force the input to have an uppercase path name

  2. echo that to get it expanded

  3. pipe the list of folder names to the tr command to change ':' into newline characters.

and Bob's your uncle. I have my solution

2 Comments

${var^^} gives uppercase
cool .. i did not find that one .. thx

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.