I am trying to delete any folder or files in my account inside the cluster using the rm command. But somehow I am not able to do this and getting an error
bash: warning: shell level (1000) too high, resetting to 1
bash: warning: shell level (1000) too high, resetting to 1
/home/.../bin/rm: fork: Cannot allocate memory
The problem is simple yet it might look complex as it shows itself in a rather unexpected way and is caused by not so simple process of command name resolution.
Let me remind you what happens when the shell executes a command like
command_name arg1 ... argn
The shell splits the command line into words separated by spaces and other metacharacters.
It takes the first word and treats at as a command name.
It checks if it contains slashes and if true treats it as a path to a command file and goes to #10
If the check in 3 fails It checks if command name is a name of a built-in command and executes the command if true
If the check in 4 fails it tries to find the command on the file system
It reads the $PATH variable and splits its value into segments separated by columns :.
For each segment it treats it as a path to a directory in the filesystem.
If the directory exists it scans the directory for an executable file name matching the command name if found it appends the command name to the directory name and treats this as a path to the command file and goes to #10
If the file is not found in any of the segments of the $PATH the shell reports an error.
The shell executes the command
command_file arg1 ... argn
Note the difference is that instead of command_name we have got command_file now.
Now what this has to do with your problem.
First your $PATH has /home/.../bin before /bin. So rm myfile becomes /home/.../bin/rm myfile instead of /bin/rm myfile.
Second your /home/.../bin/rm file is a shell script that calls rm $* or something like this. Executing this file results in a non terminating sequence of calls to /home/.../bin/rm myfile.
To check what command is being executed use the command which rm
Option 1
So one option would be to edit /home/.../bin/rm and replace all calls to rm with calls to /bin/rm in the file. Then it would not cause a non terminating sequence of calls.
Option 2
In general it is not a good idea to override standard commands unless you know very well what you are doing, so you could rename /home/.../bin/rm to /home/.../bin/myrm
Related
I want to delete content of the file main_copy.json.
I have a confusion if I used below command, will it remove files in all these directories?
[user#yyy ~]$ > /AAA/BBB/CCC/DDD/main_copy.json
The '>' character is the shell (often Bash) redirection character. You are at a command prompt of the shell, probably Bash but you don't specify. The command as you have written basically says "redirect <nothing> to the file /AAA/BBB/CCC/DDD/main_copy.json". The net result is to truncate the file to zero length, effectively deleting its contents.
Since there are no spaces in the argument to '>', bash treats it as a single argument, thus there is no possibility that your command will delete contents of any files in any of the directories in your path. In any case, the '>' redirect operator does not work with multiple arguments. So if you were to say:
[user#yyy ~]$ > /AAA /BBB/CCC/DDD/main_copy.json
The net effect would issue an error because you can't redirect to a directory, only to a file. If you were to say:
[user#yyy ~]$ > /AAA/myfile.txt BBB/CCC/DDD/main_copy.json
the shell would truncate the file myfile.txt to zero length, (or create a zero-length file if it did not exist) and then treat the second argument as an executable command, which of course would fail with something like "Permission Denied" because it's not an executable.
Hope that helps. Bash (and other shells) is a complicated beast and takes years to really learn well.
I want to send little script to remote machine by ssh
the script is
#!/bin/bash
sleep 1
reboot
but I get event not found - because the "!"
ssh 183.34.4.9 "echo -e '#!/bin/bash\nsleep 1\reboot>'/tmp/file"
-bash: !/bin/bash\nsleep: event not found
how to ignore the "!" char so script will so send successfully by ssh?
remark I cant use "\" before the "!" because I get
more /tmp/file
#\!/bin/bash
sleep 1
Use set +H before your command to disable ! style history substitution:
set +H
ssh 183.34.4.9 "echo -e '#!/bin/bash\nsleep 1\reboot>'/tmp/file"
# enable hostory expnsion again
set -H
I think your command line is not well formated. You can send this:
ssh 183.34.4.9 'echo -e "#!/bin/bash\nsleep 1\nreboot">/tmp/file'
When I say "not well formated" I mean you put ">" inside the "echo" and you forgot to add "n" before "reboot", and you put "\reboot", wich will be interpreted as "CR" (carriage return) followed by "eboot" command (which I don't think that exists).
But what did the trick here is to invert the comas changing (') with (") and viceversa.
Bash is running interactively (which means that you are feeding commands to it from the standard input and not exec(2)ing a command from a shell script) so you don't need to include the line #!/bin/bash in that case (even more, bash should just ignore it, but not the included bang, as it is part of the active history mechanism)
But why? the first two characters in an executable file (any file capable of being exec(2)ed from secondary storage, not your case) have a special meaning (for the kernel and for the shell): they are the magic number that identifies the kind of executable file the kernel is loading. This allows the kernel to select the proper executable loading routines depending on the binary executable format (and what allows you for example to execute BSD programs in linux kernels, and viceversa)
A special value for this magic numbers is composed by the two characters # and ! (in that order) that forces the kernel to read the complete first line of that file and load the executable file specified in that line instead, allowing you to execute shell scripts for different interpreters directly from the command line. And it is done on purpose, as the # character is commonly in shell script parlance a comment character. This only happens when the shell that is interpreting the commands is not an interactive shell. When the shell loads a script with those characters, it normally reads the first line also to check if it has the #! mark and load the proper interpreter, by replicating the kernel function that does this. Despite of being a comment for the shell, it does this to allow to treat as executables files that are not stored on secondary storage (the only ones the exec(2) system call can deal with), but coming from stdin (as happens to yours).
As your shell is running interactively and you do want to execute its commands without a shell change, you don't need that line and can completely eliminate it without having to disable the bang character.
Sorry, but the solution given about executing the shell with -H option will probably not be viable, as the shell executing the commands is the login shell in the target machine, so you cannot provide specific parameters to it (parameters are selected by the login(8) program and normally don't include arbitrary parameters like -H).
The best solution is to fully eliminate the #!/bin/bash line, as you are not going to exec(2) that program in the target. In case you want to select the shell from the input line (case the user has a different shell installed as login shell), it is better to invoke the wanted shell in the command line and pass it (through stdin, or making it read the shell script as a file) the shell commands you wan to execute (but again, without the #! line).
NOTE
Its important to ensure you'll execute the whole thing, so it's best to pass all the script contents in the destination target, and once assured you have passed the whole thing to execute it as a whole. Then your #! first line will be properly processed, as the executable will be run by means of an exec(2) made from the kernel.
Example:
DIRECTORY=/bla/bla
FILE=/path/to/file
OUTPUT=/path/to/output
# this is the command we want to pass through the line
cat <<EOF | ssh user#target "cat >>/tmp/shell.sh"
cd $DIRECTORY
foo $FILE >$OUTPUT
exit 0
EOF
# we have copied the script file in a remote /tmp/shell.sh
# and we are sure it has passed correctly, so it's ready
# for local execution there.
# now, execute it.
# the remote shell won't be interactive, and you'll ensure that it is /bin/bash
ssh user#target "/bin/bash /tmp/shell.sh" >remote_shell.out
A more sophisticate system is one that allows to to sign the shell script before sending, and verify the script signature before executing it, so you are protected against possible trojan horse attacks. But this is out of scope on this explanation.
Another alternative is to use the batch(2) command remotely and pass it all the commands you want executed. you'll get a sessionless executing environment, more suitable to the task you are demanding (despite the fact that you'll get the script output by email to the target user running the script)
Interactively, beware that ! triggers history expansion inside double quotes
from here: https://riptutorial.com/bash/example/2465/quoting-literal-text
my recommended solution is to use single quotes to define the string (and either escape single quotes \' or use double quotes " within the string):
ssh 183.34.4.9 'echo -e "#!/bin/bash\nsleep 1\reboot>"/tmp/file'
I found the below snippet at the .sh file of my project to define some path :
PGMPATH=`pwd|sed -e "s#/survey1##" `
What does the above line means ?
Reference of PGMPATH is used as below :
LIBS="${LIBS}:${PGMPATH}/edmz-par-api_1.4.jar"
LIBS="${LIBS}:${PGMPATH}/commons-logging.jar"
If it is telling the path where the jar file is located , please explain how it works .
So first you should know that this is two commands - pwd and sed -e "s#/survey1##" - and these two commands are being run together in a pipeline. That is, the output of the first command is being sent to the second command as input.
That is, in general, what | means in unix shell scripts.
So then, what do each of these commands do? pwd stands for "print working directory" and prints the current directory (where you ran the script from, unless the script itself had any cd commands in it).
sed is a command that's really a whole separate programming language that people do many simple text-processing commands with. The simple sed program you have here - s#/survey1## - strips the string /survey1 out of its input, and prints the result.
So the end result is that the variable PGMPATH becomes the current directory with /survey1 stripped out of it.
I am attempting to work with an existing library of code but have encountered an issue. In short, I execute a shell script (let's call this one A) whose first act is to call another script (B). Script B is in my current directory (a requirement of the program I'm using). The software's manual makes reference to bash, however comments in A suggest it was developed in ksh. I've been operating in bash so far.
Inside A, the line to execute B is simply:
. B
It uses the "dot space" syntax to call the program. It doesn't do anything unusual like sudo.
When I call A without dot space syntax, i.e.:
./A
it always errors saying it cannot find the file B. I added pwd, ls, whoami, echo $SHELL, and echo $PATH lines to A to debug and confirmed that B is in fact right there, the script is running with the same $SHELL as I am at the command prompt, the script is the same user as I am, and the script has the same search path $PATH as I do. I also verified if I do:
. B
at the command line, it works just fine. But, if I change the syntax inside A to:
./B
instead, then A executes successfully.
Similarly, if I execute A with dot space syntax, then both . B and ./B work.
Summarizing:
./A only works if A contains ./B syntax.
. A works for A with either ./B or . B syntax.
I understand that using dot space (i.e. . A) syntax executes without forking to a subshell, but I don't see how this could result in the behavior I'm observing given that the file is clearly right there. Is there something I'm missing about the nuances of syntax or parent/child process workspaces? Magic?
UPDATE1: Added info indicating that the script may have been developed in ksh, while I'm using bash.
UPDATE2: Added checking to verify $PATH is the same.
UPDATE3: The script says it was written for ksh, but it is running in bash. In response to Kenster's answer, I found that running bash -posix then . B fails at the command line. That indicates that the difference in environments between the command line and the script is that the latter is running bash in a POSIX-compliant mode, whereas the command line is not. Looking a little closer, I see this in the bash man page:
When invoked as sh, bash enters posix mode after the startup files are read.
The shebang for A is indeed #!/bin/sh.
In summary, when I run A without dot space syntax, it's forking to its own subshell, which is in POSIX-compliant mode because the shebang is #!/bin/sh (instead of, e.g., #!/bin/bash. This is the critical difference between the command line and script runtime environments that leads to A being unable to find B.
Let's start with how the command path works and when it's used. When you run a command like:
ls /tmp
The ls here doesn't contain a / character, so the shell searches the directories in your command path (the value of the PATH environment variable) for a file named ls. If it finds one, it executes that file. In the case of ls, it's usually in /bin or /usr/bin, and both of those directories are typically in your path.
When you issue a command with a / in the command word:
/bin/ls /tmp
The shell doesn't search the command path. It looks specifically for the file /bin/ls and executes that.
Running ./A is an example of running a command with a / in its name. The shell doesn't search the command path; it looks specifically for the file named ./A and executes that. "." is shorthand for your current working directory, so ./A refers to a file that ought to be in your current working directory. If the file exists, it's run like any other command. For example:
cd /bin
./ls
would work to run /bin/ls.
Running . A is an example of sourcing a file. The file being sourced must be a text file containing shell commands. It is executed by the current shell, without starting a new process. The file to be sourced is found in the same way that commands are found. If the name of the file contains a /, then the shell reads the specific file that you named. If the name of the file doesn't contain a /, then the shell looks for it in the command path.
. A # Looks for A using the command path, so might source /bin/A for example
. ./A # Specifically sources ./A
So, your script tries to execute . B and fails claiming that B doesn't exist, even though there's a file named B right there in your current directory. As discussed above, the shell would have searched your command path for B because B didn't contain any / characters. When searching for a command, the shell doesn't automatically search the current directory. It only searches the current directory if that directory is part of the command path.
In short, . B is probably failing because you don't have "." (current directory) in your command path, and the script which is trying to source B is assuming that "." is part of your path. In my opinion, this is a bug in the script. Lots of people run without "." in their path, and the script shouldn't depend on that.
Edit:
You say the script uses ksh, while you are using bash. Ksh follows the POSIX standard--actually, KSH was the basis for the POSIX standard--and always searches the command path as I described. Bash has a flag called "POSIX mode" which controls how strictly it follows the POSIX standard. When not in POSIX mode--which is how people generally use it--bash will check the current directory for the file to be sourced if it doesn't find the file in the command path.
If you were to run bash -posix and run . B within that bash instance, you should find that it won't work.
I need some help understanding following shell script line,
apphome = "`cd \`dirname $0\` && pwd && cd - >/dev/null`"
All I understand is, this is creating a variable called apphome.
This is not a valid shell code.
The shell don't allow spaces around =
For the rest, while this seems broken, it try to cd to the dir of the script itself, display the current dir & finally cd back to the latest cd place redirecting his standard output STDOUT to the /dev/null trash-bin (that's makes not any sense, cd display only on standard error STDERR when it fails, never on STDOUT)
If you want to do this in a proper a simple way :
apphome="$(dirname $0)"
That's all you need.
NOTE
The backquote
`
is used in the old-style command substitution, e.g.
foo=`command`
The
foo=$(command)
syntax is recommended instead. Backslash handling inside $() is less surprising, and $() is easier to nest. See http://mywiki.wooledge.org/BashFAQ/082
It seems to assign a command to the "apphome" variable. This command can be executed later.
dirname returns a directory portion of a file name. $0 is the name of the script this line contains (if I am not mistaken).
Now, executing dirname <name> will return a directory, and cd will use the value.
So, what it would do is execute three command in the row assuming that each one of them succeeds. The commands are:
cd `dirname [name of the script]`
pwd
cd -
First command will change directory to the directory containing your script; second will print current directory; third will take yo back to the original directory. Output of the third command will not be printed out.
In summary, it will print out a name of a directory containing the script that contains the line in question.
At least, this is how I understand it.