Pipe to export command - bash

Why does export fail when used as the last step in a command pipeline?
echo FOO=bar | xargs export
# => xargs: export: No such file or directory
I can rewrite it this way to accomplish what I want:
export `echo FOO=bar`
But why can't I use export in the first way?

export is a shell builtin and xargs expects an actual binary.

Related

Clean environment for bash shell

Is there a way to tell a bash script not to import any variables from the parent shell i.e. ignore exported variables. There is such capability in slurm --export=NONE so I wonder if there is an option I can put in the #!/bin/bash line to get similar behavior.
On linux the cleanest option I found was:
#!/usr/bin/env -S - bash
env
which for me prints:
PWD=/home/allan
SHLVL=1
_=/usr/bin/env
Another option is:
#!/usr/bin/env bash
[ -n "$HOME" ] && exec -c "$0"
env
Possible using $BASH_SOURCE[0] instead of $0 as the latter can be set by user. $BASH_SOURCE, however, is not always set. Hard-coding the script path would work but that's ugly.

How to translate an alias into a real file?

Most of the time, an alias works well, but some times, the command is executed by other programs, and they find it in the PATH, in this situation an alias not works as well as a real file.
e.g.
I have the following alias:
alias ghc='stack exec -- ghc'
And I want to translate it into an executable file, so that the programs which depending on it will find it correctly. And the file will works just like the alias does, including how it process it's arguments.
So, is there any tool or scripts can help doing this?
Here is my solution, I created a file named ghc as following:
#!/bin/sh
stack exec -- ghc "$#"
The reason why there is double quote around $# is explained here: Propagate all arguments in a bash shell script
So, is there any tool or scripts can help doing this?
A lazy question for a simple problem... Here's a function:
alias2script() {
if type "$1" | grep -q '^'"$1"' is aliased to ' ; then
alias |
{ sed -n "s#.* ${1}='\(.*\)'\$##\!/bin/sh\n\1 \"\${\#}\"#p" \
> "$1".sh
chmod +x "$1".sh
echo "Alias '$1' hereby scriptified. To run type: './$1.sh'" ;}
fi; }
Let's try it on the common bash alias ll:
alias2script ll
Output:
Alias 'll' hereby scriptified. To run type: './ll.sh'
What's inside ll.sh:
cat ll.sh
Output:
#!/bin/sh
ls -alF "${#}"

how can I turn config ini file into system environment in bash?

I have config files like below
# this is sample config file like config.ini
APP_HOME=/usr/local/bin
DATABASE_DIR=/usr/local/database
Normally in order to be access as system environment, it shall use export in front
# this is sample config file like config.rc
export APP_HOME=/usr/local/bin
export DATABASE_DIR=/usr/local/database
And I can
$ source config.rc
$ echo "APP_HOME is $APP_HOME"
APP_HOME is /usr/local/bin
Now Which is the easiest way one line command to turn config file config.ini into system environment ? could be combine with sed/awk command
You can tell the shell to automatically export variables, if you really do need this to be exported.
set -a # turn on automatic export
source config.ini # execute all commands in the file
set +a # turn off automatic export
sed 's/^/export /g' config.ini > config.sh && source config.sh
The sed command add 'export ' to the beginning for each line of config.ini and then redirect the output to config.sh, then the source shell builtin read and execute the exports in the current shell environment.
To export the variables in that file after you source if:
export $(grep -oP '^\s*\K[_[:alpha:]]\w+(?==)' config.ini)
one line:
cat 1.ini | awk '{print "export "$0}'

Bash command as variable

I am trying to store the start of a sed command inside a variable like this:
sedcmd="sed -i '' "
Later I then execute a command like so:
$sedcmd s/$orig_pkg/$package_name/g $f
And it doesn't work. Running the script with bash -x, I can see that it is being expanded like:
sed -i ''\'''\'''
What is the correct way to express this?
Define a shell function:
mysed () {
sed -i "" "$#"
}
and call it like this:
$ mysed s/$orig_pkg/$package_name/g $f
It works when the command is only one word long:
$ LS=ls
$ $LS
But in your case, the shell is trying the execute the program sed -i '', which does not exist.
The workaround is to use $SHELL -c:
$ $SHELL -c "$LS"
total 0
(Instead of $SHELL, you could also say bash, but that's not entirely reliable when there are multiple Bash installations, the shell isn't actually Bash, etc.)
However, in most cases, I'd actually use a shell function:
sedcmd () {
sed -i '' "$#"
}
Why not use an alias or a function? You can do alias as
alias sedcmd="sed -i '' "
Not exactly sure what you're trying to do, but my suggestion is:
sedcmd="sed -i "
$sedcmd s/$orig_pkg/$package_name/g $f
You must set variables orig_pkg package_name and f in your shell first.
If you're replacing variable names in a file, try:
$sedcmd s/\$orig_pkg/\$package_name/g $f
Still f must be set to the file name you're working on.
This is the right way for do that
alias sedcmd="sed -i ''"
Obviously remember that when you close your bash, this alias will be gone.
If you want to make it "permanent", you have to add it to your .bashrc home file (if you want to make this only for a single user) or .bashrc global file, if you want to make it available for all users

Get current directory or folder name (without the full path)

How could I retrieve the current working directory/folder name in a bash script, or even better, just a terminal command.
pwd gives the full path of the current working directory, e.g. /opt/local/bin but I only want bin.
No need for basename, and especially no need for a subshell running pwd (which adds an extra, and expensive, fork operation); the shell can do this internally using parameter expansion:
result=${PWD##*/} # to assign to a variable
result=${result:-/} # to correct for the case where PWD=/
printf '%s\n' "${PWD##*/}" # to print to stdout
# ...more robust than echo for unusual names
# (consider a directory named -e or -n)
printf '%q\n' "${PWD##*/}" # to print to stdout, quoted for use as shell input
# ...useful to make hidden characters readable.
Note that if you're applying this technique in other circumstances (not PWD, but some other variable holding a directory name), you might need to trim any trailing slashes. The below uses bash's extglob support to work even with multiple trailing slashes:
dirname=/path/to/somewhere//
shopt -s extglob # enable +(...) glob syntax
result=${dirname%%+(/)} # trim however many trailing slashes exist
result=${result##*/} # remove everything before the last / that still remains
result=${result:-/} # correct for dirname=/ case
printf '%s\n' "$result"
Alternatively, without extglob:
dirname="/path/to/somewhere//"
result="${dirname%"${dirname##*[!/]}"}" # extglob-free multi-trailing-/ trim
result="${result##*/}" # remove everything before the last /
result=${result:-/} # correct for dirname=/ case
Use the basename program. For your case:
% basename "$PWD"
bin
$ echo "${PWD##*/}"
​​​​​
Use:
basename "$PWD"
OR
IFS=/
var=($PWD)
echo ${var[-1]}
Turn the Internal Filename Separator (IFS) back to space.
IFS=
There is one space after the IFS.
You can use a combination of pwd and basename. E.g.
#!/bin/bash
CURRENT=`pwd`
BASENAME=`basename "$CURRENT"`
echo "$BASENAME"
exit;
How about grep:
pwd | grep -o '[^/]*$'
This thread is great! Here is one more flavor:
pwd | awk -F / '{print $NF}'
basename $(pwd)
or
echo "$(basename $(pwd))"
I like the selected answer (Charles Duffy), but be careful if you are in a symlinked dir and you want the name of the target dir. Unfortunately I don't think it can be done in a single parameter expansion expression, perhaps I'm mistaken. This should work:
target_PWD=$(readlink -f .)
echo ${target_PWD##*/}
To see this, an experiment:
cd foo
ln -s . bar
echo ${PWD##*/}
reports "bar"
DIRNAME
To show the leading directories of a path (without incurring a fork-exec of /usr/bin/dirname):
echo ${target_PWD%/*}
This will e.g. transform foo/bar/baz -> foo/bar
echo "$PWD" | sed 's!.*/!!'
If you are using Bourne shell or ${PWD##*/} is not available.
Surprisingly, no one mentioned this alternative that uses only built-in bash commands:
i="$IFS";IFS='/';set -f;p=($PWD);set +f;IFS="$i";echo "${p[-1]}"
As an added bonus you can easily obtain the name of the parent directory with:
[ "${#p[#]}" -gt 1 ] && echo "${p[-2]}"
These will work on Bash 4.3-alpha or newer.
There are a lots way of doing that I particularly liked Charles way because it avoid a new process, but before know this I solved it with awk
pwd | awk -F/ '{print $NF}'
For the find jockeys out there like me:
find $PWD -maxdepth 0 -printf "%f\n"
i usually use this in sh scripts
SCRIPTSRC=`readlink -f "$0" || echo "$0"`
RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
echo "Running from ${RUN_PATH}"
...
cd ${RUN_PATH}/subfolder
you can use this to automate things ...
Just use:
pwd | xargs basename
or
basename "`pwd`"
Below grep with regex is also working,
>pwd | grep -o "\w*-*$"
If you want to see only the current directory in the bash prompt region, you can edit .bashrc file in ~. Change \w to \W in the line:
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u#\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
Run source ~/.bashrc and it will only display the directory name in the prompt region.
Ref: https://superuser.com/questions/60555/show-only-current-directory-name-not-full-path-on-bash-prompt
I strongly prefer using gbasename, which is part of GNU coreutils.
Just run the following command line:
basename $(pwd)
If you want to copy that name:
basename $(pwd) | xclip -selection clipboard
An alternative to basname examples
pwd | grep -o "[^/]*$"
OR
pwd | ack -o "[^/]+$"
My shell did not come with the basename package and I tend to avoid downloading packages if there are ways around it.
You can use the basename utility which deletes any prefix ending in / and the suffix (if present in string) from string, and prints the
result on the standard output.
$basename <path-of-directory>
Just remove any character until a / (or \, if you're on Windows). As the match is gonna be made greedy it will remove everything until the last /:
pwd | sed 's/.*\///g'
In your case the result is as expected:
λ a='/opt/local/bin'
λ echo $a | sed 's/.*\///g'
bin
Here's a simple alias for it:
alias name='basename $( pwd )'
After putting that in your ~/.zshrc or ~/.bashrc file and sourcing it (ex: source ~/.zshrc), then you can simply run name to print out the current directories name.
The following commands will result in printing your current working directory in a bash script.
pushd .
CURRENT_DIR="`cd $1; pwd`"
popd
echo $CURRENT_DIR

Resources