Bash - How to Pipe Result from the Which Command to Cd

bash - how to pipe result from the which command to cd

You use pipe in cases where the command expects parameters from the standard input. ( More on this ).

With cd command that is not the case. The directory is the command argument. In such case, you can use command substitution. Use backticks or $(...) to evaluate the command, store it into variable..

path=`which oracle`
echo $path # just for debug
cd $path

although it can be done in a much simpler way:

cd `which oracle` 

or if your path has special characters

cd "`which oracle`"

or

cd $(which oracle)

which is equivalent to backtick notation, but is recommended (backticks can be confused with apostrophes)

.. but it looks like you want:

cd $(dirname $(which oracle))

(which shows you that you can use nesting easily)

$(...) (as well as backticks) work also in double-quoted strings, which helps when the result may eventually contain spaces..

cd "$(dirname "$(which oracle)")"

(Note that both outputs require a set of double quotes.)

Why doesn't the cd command work when trying to pipe it with another command

cd takes no input and produces no output; as such, it does not make sense to use it in pipes (which take output from the left command and pass it as input to the right command).

Are you looking for ls -l ; cd /somewhere?

Another option (if you need to list the target directory) is:

cd /somewhere && ls -l

The '&&' here will prevent executing the second command (ls -l) if the target directory does not exist.

why isnt it piping to the cd command?

The cd command doesn't read standard input, so anything that you pipe to it will be ignored.

The closest to what your command is (literally) trying to do will be this:

cd `find -size 1033c`

... except that you are liable to 'cd' to a file (which will fail!) or not find a directory with that size ... leading to you (silently) cd-ing to the user's home directory.

And also your find command is missing a directory to search!


Based on your comments, you are trying to cd to the directory containing a file with a given size. If we can assume that there will only ever be one such file, then the following should work:

FILE=`find . -size 1033c -type f` 
cd `dirname $FILE`

If there could be more than one match, then you need to do something like this ... which should cd to the directory with the first matching file.

FILE=`find . -size 1033c -type f -print -quit` 
cd `dirname $FILE`

Unix: (pwd now: ~) from cd command?

When you "pipe" input from one process or job to another, you are also spawning another process. Both of these jobs are said to be "children" of main process which called them (usually the process running the terminal emulator).

# 2 process are run here
# first job: echo
# second job: ls
echo "/home/" | ls

Looking at the source for zsh, it looks like when the cwd of a job differs from the original cwd of the job after execution, it will notify the user with a message like (pwd now: foo). This helps to ensure that the user knows exactly where the are in the directory tree, which is especially useful when they may not have intended to do so. Below is taken from jobs.c where the cwd (referred to as pwd) where among other things, the cwd (referenced as pwd) are compared before printing the (pwd now: foo) message. If they are different the message is printed, if they are equal it is not.

if ((lng & 4) || (interact && job == thisjob &&
jn->pwd && strcmp(jn->pwd, pwd))) {
doneprint = 1;
fprintf(fout, "(pwd %s: ", (lng & 4) ? "" : "now");
fprintdir(((lng & 4) && jn->pwd) ? jn->pwd : pwd, fout);
fprintf(fout, ")\n");
fflush(fout);
}

When you pipe something into ch you are changing the cwd in a child process and some of the normal checks which hide this message when calling cd directly are bipassed. Otherwise cd words in the same way.

# changes directories into ./test
echo "test" | cd
# (pwd now: /path/to/test)

# changes directory to home
echo | cd
# (pwd now: /home/<username>)

# calling this again will not echo the message, since the cwd is already
# the home directory and no change occurs
echo | cd

As for why the cwd is changed to the home directory (~) it is due to how cd behaves when no paths are given as an argument. Unlike a lot of linux commands, cd does not read from stdin for paths to move into. Due to this, piping into cd will simply populate stdin for cd, but that content is ignored. Because of this piping into cd is the same as just calling cd on its own.

# these next lines produce the same behavior
echo path/to/dir/test | cd
cd

When cd does not receive a path to move to, it will move you to your home directory (referenced on linux systems as ~)

# each of these lines produce the same behavior
cd /home/<username>
cd ~
cd

How to pipe output of a command that expects file argument

Some programs have special handling for -. For example, you can tell tar to write to stdout so it can be used in a pipeline. This would create a tarball locally and untar it remotely without the tarball ever being written to disk:

tar -cf - *.txt | ssh user@host tar -C /dir/ -xf -

You can use /dev/stdout with nearly all programs, as long as they don't need a seekable file.

command --save-to-file /dev/stdout

Can I pipe between commands, and run the programs from different directories?


command arg1 | ( cd /other_dir ; command arg2 )

(…) executes a command in a subshell. cd is a shell builtin command, not a 'real process'. ( cd X ; command ) will start a new sub-shell, cd into X, then run command. command is running as a process, but in a different directory.

Going forward it's better to have commands that can take a directory as an argument (and if not defined, default to the current working directory). Then you could have the simple solution of command arg1 | command --dir=/other_dir arg2

How would I tar extract pipe cd?

Pipes link up the output of one program to the input of another - just how a pipe sends the output from your garden tap (faucet) to your lawn sprinkler.

A minus sign (-) is a shortcut meaning either input or output - which one it means depends on position/context.

Your wget command grabs some data from the web and, because of the -, sends it through a pipe to tar.

The tar command reads that data from the pipe, because of its -, and creates some files and directories.

The tar command doesn't send anything down any further pipes, so there'd be nothing for cd to read. You also don't know the name of the directory you want to go to. If you did, you could go there as a follow-up action like this:

wget ... - | tar ... - ; cd SOMEWHERE

Or you could make it conditional on the tar command running successfully with:

wget ... - | tar ... - && cd SOMEWHERE

Piping `cd` or `popd` output prevents changing directories?

In bash, dash and ash, each command in a pipeline runs in a subshell.

In zsh, ksh, and bash with shopt -s lastpipe, all except the last command in the pipeline run in subshells.

Since cd -- as well as variables, shell options, ulimits and new file descriptors -- only affects the current process, their effects will not affect the parent shell.

Examples:

# Doesn't change directory
cd foo | cat
pwd

# Doesn't set $bar on default bash (but does on zsh and ksh)
echo foo | read bar
echo "$bar"

# Doesn't change the ulimit
ulimit -c 10000 2>&1 | grep "not permitted"
ulimit -c

The same also applies to other things that generate subshells. None of the following will change the directory:

# Command expansion creates a subshell
echo $(cd foo); pwd

# ( .. ) creates a subshell
( cd foo ); pwd

# Backgrounding a process creates a subshell
cd foo & pwd

To fix it, you have to rewrite your code to run anything that affects the environment in the main shell process.

In your particular case, you can consider using process substitution:

popd > /dev/null 2>   >(toerr) && lsd

This has the additional benefit of only running lsd when popd is successful, rather than when toerr is successful like your version does.



Related Topics



Leave a reply



Submit