Bash scripting and && operator
I'm running into a problem that I do not understand in the slightest. In a script, I have a command like so:
mkdir ../build-dir && cd ../build-dir When the script executes that line, it errors with this: mkdir: cannot create directory `../build-dir': File exists I look and sure enough, the directory does exist, but what's causing this error? Is the shell trying to execute the command twice? I know, I could break the command into two separate statements, but being able to use the && is key to the way I want to implement the script. I don't want to break it up unless I absolutely have to since it will add a significant amount of complexity. |
Certainly not ...
You might, however, want to check whether the directory exists before you create it :) Or did you do rm -Rf ../build-dir before running the script? Cheers, Tink |
why are you making the directory a level up from your current location? couldn't you just go to the location and issue the command with just mkdir build-dir && cd build-dir? anyways, i don't know if i understand...you say you issue this command and you get that error, and you look and the directory is there...so wouldn't that explain why your getting the error?
|
Sorry, I guess I didn't clarify. The script is trying to automate compiling packages. It un-tar's the source, goes into the source tree, applies any patches, and then wants to execute the command I gave:
mkdir ../build-dir && cd ../build-dir The directory does not exist prior to this command being executed. I've verified that more than once in two different ways: 1) verifying prior to the script running 2) exiting from the script immediately before the command is executed In both cases, the directory does not exist. So, the mkdir portion does in fact create the directory as expected, but I get the error mentioned and I can't explain it. For full disclosure, the above command resides in a shell variable. assigned similar to: command="mkdir ../build-dir && cd ../build-dir" and then executed later simply as: ${command} I'm wondering if the ampersands need to be escaped, or if they simply cannot be used in this fashion. I've looked online for a script that does something like this, but all I can find is the && operator being used to combine test cases; not actual commands. Edit: Hmmmm... I guess I have to take that back... tests ARE commands... :) |
Code:
if [ \! -d ../goobers ]; then mkdir ../goobers && \ Code:
command='if [ \! -d ../goobers ]; then mkdir ../goobers && echo |
Ok, it's been a while, but hopefully we can hammer this out. I would REALLY like to keep the command in the form "mkdir ../build-dir && cd ../build-dir". At this point, I've played with the code some and just cannot figure it out. So, I'm going to post what I think are the important parts and hope SOMEBODY can see an error or at least try it out on their system to see if they get the same problem. You are warned, this is a LOT of code, and I would be VERY appreciative if someone can identify my problem or confirm the possibility of a bug in bash's parsing. I can also email the whole script if someone thinks it would be necessary.. it's too long to post though.
Code:
#!/bin/bash |
I think it's a misunderstanding in the way the shell expands variables/parameters (I'm not saying I completely understand it either). My solution, for now, would be, if you really don't want to use if statements and the "eval" built-in, to define BINUTILS_STATIC_PRE as a command rather than a parameter:
Code:
BINUTILS_STATIC_PRE () { mkdir ../build-dir && cd ../build-dir; } |
Well, the short of it is, I got it to work.
I tried the function definition like you suggested, but I couldn't get it to work. I understood the concept, but maybe I didn't code it up properly. Anyway, here's the "solution" that I'll probably keep: Code:
if [ ${TEST_RUN} -ne ${TRUE} ] ; then |
Use eval
I was poking around old posts about scripting & this piqued my interest. You have probably long since discovered this answer &/or no longer have the problem. In any case, for the benefit of those who might find it useful, I believe the answer to the orig. problem is "eval".
Here is the orig. problem, as I understand it: Code:
[test]$ X="mkdir ../build-dir && cd ../build-dir" Now clean up & verify it: Code:
[test]$ cd ../test ; rm -rf ../build-dir; ls .. Code:
[test]$ eval ${X} I can't explain exactly why this works when a raw '$X' does not. I am sure it has something to do w/ the subtle details of how bash parses & expands the various tokens. |
What about:
(mkdir ../build-dir) && (cd ../build-dir) |
As I understand it, that's not quite the same. The parentheses create a subshell to execute the command in. That will work fine for the mkdir command, but it won't achieve the cd correctly. The subshell will change working directory and then get destroyed. The parent script's execution never changes directory; just the subshell which no longer exists.
Honestly though, I'm not at a computer to check. So I may be completely wrong. As a side note, I never explicitly said what this script was for. Anyone familiar with the LFS project would likely recognize the steps/commands. It was an attempt to automate the LFS build process, but has since been abandoned. So there's no need to "solve" the problem any more except from a curiosity standpoint. |
Suggest you check here:
http://www.tldp.org/LDP/abs/abs-guide.txt.gz There a lot of reference to the double ampersand and you may get some new insights into its usage. |
Quote:
Code:
[ ! -d ../build_dir ] && mkdir ../build_dir ; cd ../build_dir |
Nope, the command conditionally changes directory if the mkdir exits successfully. Bash implements logical "short circuits" when it comes to that operator. To illustrate, let me use a few examples (not shell syntax, just regular logic):
Logical AND if A & B then C The system evaluates whether A is true or false. If A is true, then B needs to be evaluated. If A is false, there's nothing that B can equal to make the "A & B" statement true. So there's no need to evaluate B; the statement as a whole is false. Logical OR if A | B then C The system evaluates whether A is true or false. If true, then B is skipped. Again, because if A is true, there's nothing that B can equal to make "A | B" false. If A is false, then B is evaluated. In the command above, the equivalent of a logical AND is used. If it's picked apart, we get: if A & B then C A = "mkdir ../build-dir" B = "cd ../build-dir" There is no C, because "A && B" is interested only in accomplishing the "side effects" of the commands from a logical standpoint Evaluate A: "mkdir ../build-dir" If that command returns true, then the script needs to evaluate B ("cd ../build-dir"). If the mkdir command returns false, we skip evaluation of B. In this case, there's an error for A, and we don't want to cd into the directory because it may not exist. So by skipping evaluation of B (since A was false) the script avoids commiting an error, which is what's desired. Clear as mud, I know... sorry. |
Quote:
BTW, thanks for the patronising logic lesson - I've been writing shellscripts since 1980, and I think I've already got the hang of the shell's "short-circuiting" by now. |
All times are GMT -5. The time now is 07:45 PM. |