Linux command to delete file content - linux

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.

Related

rm command not able to use inside cluster

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

Recursively adding bash scripts as commands in Linux?

I'm pretty new to Bash and Linux in general. I've created a couple scripts that I would like to be able to use by typing the command, rather than the directory and the executable file. I'm using Debian Jessie if that makes a difference.
The path to one of my scripts is ~/Scripts/DIR_1/My_Script.sh while another is in ~Scripts/DIR_2/My_Other_Script.sh. I would like ALL of the scripts contained withing the Scripts directory to be indexed as commands regardless of directory/path depth.
I've appended this text to the end of my .bashrc file...
PATH=${PATH}:$(find ~/Scripts -type d | sed '/\/\\./d' | tr '\n' ':' | sed 's/:$//')
Since I'm pretty new to this kind of thing, I had to steal that line from here.
When I try to run My_Script from the command line withing a sub directory of my home folder (or anywhere else for that matter) I get My_Script: command not found
I will readily admit that I might have misunderstood the process of adding a bash script to the command line.
How do I recursively add bash scripts as commands? What is wrong with the process I'm currently using?
I think your issue is that you're not putting the .sh, that is part of your file name.
Normally, pressing tab after having typed only the first letter should complete the command up to the point where there is an ambiguity (or completely if there's none). In case of ambiguity, pressing tab a second time shows the options. So in your case, if you type My<tab><tab> you should have options My_Script.sh and My_Other_Script.sh displayed. And if you type My_Script<tab> it should complete by putting My_Script.sh
Edit
I forgot to precise that you can check the value of PATH by doing echo $PATH. This will allow you to check that the command you copied did what you wanted.

What does this shell script line of code mean

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.

Shell Script - Linux

I want to write a very simple script , which takes a process name , and return the tail of the last file name which contains the process name.
I wrote something like that :
#!/bin/sh
tail $(ls -t *"$1"*| head -1) -f
My question:
Do I need the first line?
Why isn't ls -t *"$1"*| head -1 | tail -f working?
Is there a better way to do it?
1: The first line is a so called she-bang, read the description here:
In computing, a shebang (also called a
hashbang, hashpling, pound bang, or
crunchbang) refers to the characters
"#!" when they are the first two
characters in an interpreter directive
as the first line of a text file. In a
Unix-like operating system, the
program loader takes the presence of
these two characters as an indication
that the file is a script, and tries
to execute that script using the
interpreter specified by the rest of
the first line in the file
2: tail can't take the filename from the stdin: It can either take the text on the stdin or a file as parameter. See the man page for this.
3: No better solution comes to my mind: Pay attention to filenames containing spaces: This does not work with your current solution, you need to add quotes around the $() block.
$1 contains the first argument, the process name is actually in $0. This however can contain the path, so you should use:
#!/bin/sh
tail $(ls -rt *"`basename $0`"*| head -1) -f
You also have to use ls -rt to get the oldest file first.
You can omit the shebang if you run the script from a shell, in that case the contents will be executed by your current shell instance. In many cases this will cause no problems, but it is still a bad practice.
Following on from #theomega's answer and #Idan's question in the comments, the she-bang is needed, among other things, because some UNIX / Linux systems have more than one command shell.
Each command shell has a different syntax, so the she-bang provides a way to specify which shell should be used to execute the script, even if you don't specify it in your run command by typing (for example)
./myscript.sh
instead of
/bin/sh ./myscript.sh
Note that the she-bang can also be used in scripts written in non-shell languages such as Perl; in the case you'd put
#!/usr/bin/perl
at the top of your script.

rm fails to delete files by wildcard from a script, but works from a shell prompt

I've run into a really silly problem with a Linux shell script. I want to delete all files with the extension ".bz2" in a directory. In the script I call
rm "$archivedir/*.bz2"
where $archivedir is a directory path. Should be pretty simple, shouldn't it? Somehow, it manages to fail with this error:
rm: cannot remove `/var/archives/monthly/April/*.bz2': No such file or directory
But there is a file in that directory called test.bz2 and if I change my script to
echo rm "$archivedir/*.bz2"
and copy/paste the output of that line into a terminal window the file is removed successfully. What am I doing wrong?
TL;DR
Quote only the variable, not the whole expected path with the wildcard
rm "$archivedir"/*.bz2
Explanation
In Unix, programs generally do not interpret wildcards themselves. The shell interprets unquoted wildcards, and replaces each wildcard argument with a list of matching file names.
if $archivedir might contain spaces, then rm $archivedir/*.bz2 might not do what you
You can disable this process by quoting the wildcard character, using double or single quotes, or a backslash before it. However, that's not what you want here - you do want the wildcard expanded to the list of files that it matches.
Be careful about writing rm $archivedir/*.bz2 (without quotes). The word splitting (i.e., breaking the command line up into arguments) happens after $archivedir is substituted. So if $archivedir contains spaces, then you'll get extra arguments that you weren't intending. Say archivedir is /var/archives/monthly/April to June. Then you'll get the equivalent of writing rm /var/archives/monthly/April to June/*.bz2, which tries to delete the files "/var/archives/monthly/April", "to", and all files matching "June/*.bz2", which isn't what you want.
The correct solution is to write:
rm "$archivedir"/*.bz2
Your original line
rm "$archivedir/*.bz2"
Can be re-written as
rm "$archivedir"/*.bz2
to achieve the same effect. The wildcard expansion is not taking place properly in your existing setup. By shifting the double-quote to the "front" of the file path (which is legitimate) you avoid this.
Just to expand on this a bit, bash has fairly complicated rules for dealing with metacharacters in quotes. In general
almost nothing is interpreted in single-quotes:
echo '$foo/*.c' => $foo/*.c
echo '\\*' => \\*
shell substitution is done inside double quotes, but file metacharacters aren't expanded:
FOO=hello; echo "$foo/*.c" => hello/*.c
everything inside backquotes is passed to the subshell which interprets them. A shell variable that is not exported doesn't get defined in the subshell. So, the first command echoes blank, but the second and third echo "bye":
BAR=bye echo `echo $BAR`
BAR=bye; echo `echo $BAR`
export BAR=bye; echo `echo $BAR`
(And getting this to print the way you want it in SO takes several tries is apparently impossible...)
The quotes are causing the string to be interpreted as a string literal, try removing them.
I've seen similar errors when calling a shell script like
./shell_script.sh
from another shell script. This can be fixed by invoking it as
sh shell_script.sh
Why not just rm -rf */*.bz2? Works for me on OSX.

Resources