ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I want to modify the source of cd (the 'change directory' command) to do a little extra for me, but I can't seem to find the source code anywhere.. maybe I didn't look for it well enough, and googling "source code cd" and variants of that doesn't exactly help... I was wondering if anyone knew where I could find the src?
The current working directory is part of the environment. 'cd' *cannnot* be implemented as an external program. A simple way to demonstrate this is to run
Code:
#include <unistd.h>
int main()
{
chdir("/");
return 0;
}
The main take-away, is that 'cd' must always be implemented as a shell builtin.
Oooh I see, so the 'cd' command is part of bash (for example) and not an actual program?
That makes sense, because "which cd" returns an error (and also because a program can't change the directory of its parent process, or so I understand it...)
The thing I wanted to do is modify cd so that it saves the current directory in a file, ~/.cd_history, and then changes to the specified dir. Then what I can do is call cdb (cd-back) or cdf (cd-forward) and go back (or forward) to previous directories.
Not much, but it could prove useful, I suppose...
So how would I go about implementing this then? I tried writing both a perl and a bash script before realizing that they cannot change the directory of the parent process...
So how would I go about implementing this then? I tried writing both a perl and a bash script before realizing that they cannot change the directory of the parent process...
The trick is twofold.
Hang on to that bash script. That's the one you want to use.
Instead of calling it the normal way, use the "." command to call it.
For a proof of concept, run this script:
Code:
cat > 2.sh <<EOD
cd /
EOD
echo before calling the script, the current working directory is:
pwd
. ./2.sh
echo after calling the script, the current working directory is:
pwd
You'll get output something like this:
Code:
before calling the script, the current working directory is:
/u/wally/tuesday/2
after calling the script, the current working directory is:
/
Thought I'd point that out so you won't spend great effort trying to do this if it's a must-have. Of course, if you're doing that as a learning exercise, by all means, go forth and program!
Put the following into your .bashrc or .bash_profile
Code:
# #-- CD History
CDHIST[0]=$PWD
alias cd=cdh
if [ -e $HOME/.cdhist ]
then
. $HOME/.cdhist
fi
cdh()
{
typeset -i cdlen i
if [ "$#" -eq 0 -a "$PWD" != "$HOME" ]
then
set -- $HOME
fi
cdlen=${#CDHIST[*]}
case "$@" in
-l)
i=0
while ((i < cdlen))
do
printf "%3d %s\n" $i "${CDHIST[i]}"
((i = i + 1))
done
return ;;
-[0-9]|-[0-9][0-9])
i=${1#-}
'cd' "${CDHIST[i]}"
return
;;
*)
if [ ! -e "$@" ]
then
echo "bash: cd: $@: No such file or directory"
return
fi
if [ ! -d "$@" ]
then
echo "bash: cd: $@: Not a directory"
fi
'cd' "$@"
;;
esac
CDHIST[cdlen]="$PWD"
return
}
#-- end CD History
Restart your bash process. Now, when you run 'cd' it preserves every directory to which you've been.
If you want to see the list, cd -l
If you want to change to a specific (lets say #10) directory, cd -10
The big problem with pushd/popd is that you can't run to an arbitrary directory in the stack. You MUST pop down the line.
I haven't tested orgcandman's solution, but it doesn't pay sufficient attention to just which file should contain it. Also, you can't export an alias; more on exporting stuff, and initialization files, below.
*** the basic solution (but nowhere near complete) ***
Put the following into your ~/.bash_profile, or your ~/.profile. If you have neither one, pick either one.
Code:
cd() {
echo $PWD >> ~/.cd_history
if [ $# -lt 1 ]
then
builtin cd ~
else
builtin cd "${1}"
fi
}
export -f cd
Then try it out. You'll find that it works.
chrism01's second link advises that last export statement. But it doesn't explain why.
*** what happens if you don't use export ***
Here's why you need it. You don't need it when you're in your login window. But if, say, you do this:
Code:
xterm &
and move to that other window, you'll find that the new definition disappears. That's because neither .profile nor .bash_profile is run on any shell except the login shell. (Those are the customary terms; what they really mean is "any instantiation of the shell except at login".)
What you use for other shells is ~/.bashrc. So why not just put the code there? Because .bashrc is not used for login shells, just for the others!
This can actually be a feature. You can decide to have one operating context for login shells, and another for other shells, and this is a clean way to divide them. But I think most people won't care. If that's the case, you might be tempted to make .bashrc duplicate .profile (or .bash_profile), or the other way around. Don't do this. Why? Because when you make a change to one, you might forget to make a change to the other. The solution to this is either to put this:
Code:
. ~/bashrc
in your profile file, and put all your real stuff in .bashrc, or the other way around. (A link, symbolic or otherwise, from one to the other sounds useful, but it complicates things if you decide later that the two files should have slightly different content.)
I'm boring you with all these details because it's a good idea to know the relationships among these initialization files. You won't need to know this for your current purposes, however, because you're going to use the export statement, right?
*** what the export statement doesn't solve: .profile versus .bash_profile ***
If your home directory contains either .profile or .bash_profile , but not both, that file will be executed whenever you fire up a login shell.
But.
If both files exist, it will run .bash_profile and ignore .profile, without warning.
This is not a feature. This is a dangerous bug. Steam is coming out of my ears as I write this.
*** this dangerous bug doesn't affect your situation ***
I gather from your reference to /etc/profile that you want this to be a universal feature on your system. Go ahead and put your code there. Because you'll be using export, you'll not need /etc/bashrc. This is a good thing, because bash doesn't look at any such file. And it doesn't look at /etc/bash_profile, either, so you don't have to worry about that.
Oooh I see, so the 'cd' command is part of bash (for example) and not an actual program?
That makes sense, because "which cd" returns an error (and also because a program can't change the directory of its parent process, or so I understand it...)
just a bit late and possibly already out-of-context , but you could use the type command of the bash to verify that 'cd' is built-in:
Code:
[root@andihellmund 4.4.3]# type cd
cd is a shell builtin
[root@andihellmund 4.4.3]# type gcc
gcc is hashed (/usr/bin/gcc)
If both files exist, it will run .bash_profile and ignore .profile, without warning.
This is not a feature. This is a dangerous bug. Steam is coming out of my ears as I write this.
erm, it *is* a feature, just like the difference between .bashrc and .bash_profile. It allows me to have use a different context for a bash login shell than if I happen to use zsh or ksh as a login shell. This behaviour is quite clearly documented, and I'd find it a *bug* if it tried to source more than one system profile and more than one user profile, because that assumes a workflow for the user that may not really be the case.
Also, DON'T export functions like cd. Although it might sound nifty, exporting these functions also affects shell scripts and may cause them to fail in strange and mysterious ways.
Wow, that's a lot more information than I expected! :P Thanks guys!
I will look into the various solutions available and whatnot, but yeah, it was mostly just as an exercise I guess..
Anyways, thanks again for all the info guys Much appreciated!
It allows me to have use a different context for a bash login shell than if I happen to use zsh or ksh as a login shell. This behaviour is quite clearly documented, and I'd find it a *bug* if it tried to source more than one system profile
I would consider that a bug also. But the problem with saying the current situation is "clearly documented" is that, in the real world, it's easy for someone to be confused by this. IMO.
I'm not saying that bash should read both files. That would be horrendous. I'm saying that if bash finds both files, it should heed .bash_profile, just as it does now, but issue a warning to stderr unless there's something in the .bash_profile which indicates that the warning should not be issued. That covers both those who read all the documentation and those who could have missed this.
Quote:
Originally Posted by tuxdev
Also, DON'T export functions like cd. Although it might sound nifty, exporting these functions also affects shell scripts and may cause them to fail in strange and mysterious ways.
A user (or, for /etc/profile, a site admistrator) could make the judgment call that a function named cd has been sufficiently well written, and sufficiently transparent, that no scripts will be broken, and take this risk. I sense that this is a religious issue.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.