Shell Script to Find ownerless Files - bash

I am trying to figure out how to write a shell script for Solaris 10 that finds all of the ownerless files on the box using an if statement, prints the file names and locations and assigns them the root owner if they are ownerless.
Fairly new with unix in general and shell scripting.
Please help.

Something like this should work:
find / -nouser -exec echo chown root '{}' \;
Once you are satisfied with the output, remove the echo from the line above and re-run.
Dissecting the command above:
find - The command you are executing
/ - Start looking for files at / (so look at all files)
-nouser - Find only files whose numeric user ID doesn't have a corresponding entry in /etc/passwd
-exec - Run the following command for all of the files that we found based on the previous conditions
echo chown root '{}' \; - The command to run for each of the matched files. {} is replaced with the full filename and the ; is escaped so that find sees it rather than the shell seeing it as an end-of-command marker.
The echo is there so that you can validate that the appropriate commands will run before running potentially the chown command which might screw things up.
So you run the above once, make sure that the commands it prints out are good, and then you re-run the find command above but you remove the echo so that chown is actually executed instead of just bring printed out.

Related

failed to delete multiple folders inside one folder except 3 folders using rm

I am trying to delete all folders inside a folder except 2 3 using rm. But, command doesnt work with below error
Tried using escape character, but it will not delete folders.
Any solutions?
EDIT 1
Using double quotes after parentheses is a not working
EDIT 2
using shopt is also not working
Note 1: Shell Option needs to be set first for extglob. I had the same issue running my script until I added it to my .sh file. Link below is a reference from Unix stack exchange.
https://unix.stackexchange.com/questions/153862/remove-all-files-directories-except-for-one-file
Full Working Script: test.sh
shopt -s extglob;
cd Test;
rm -rfi !("atest3"|"atest2")
Note 2: If working in Ansible, the default shell is SH vs BASH. Due to this, you may need to call the script with BASH or use solutions found in this answer to get it working. The script below works in BASH. In ZSH it may require a setopt command similar to extglob.
Script call from SH basic terminal using bash
The following command will remove (rm) by force (f) recursively (r) and interactively (i) all files or folders except (! bang operator) for the file or folders strings ('arg1'|'arg2'|...) listed as piped (|) arguments.
If a nested files and folders is passed as an arg, but the parent is not, that file or folder will be deleted:
rm -rfi !('atest2'|'atest3')
Example of using rm with -rf flags and bang operator to prevent passed string arguments being deleted
I am leaving the below commands as examples from an earlier answer I wrote.
This command will create multiple folders:
mkdir {test1,atest2,atest3,atest4}
And this command will remove the folders:
rm -rfi {test1,atest2,atest3,atest4}
Example of mkdir with args and rm -rfi with args shell command

Cannot properly execute bash script because of a redirection in an environment where root is the owner

My script is executable and I run it as sudo. I tried many workarounds and alternatives to the ">>" operator but nothing seemed to work properly.
My script:
#! /bin/bash
if [[ -z "$1" || -z "$2" ]]; then
exit 1
else
root=$1
fileExtension=$2
fi
$(sudo find $root -regex ".*\.${fileExtension}") >> /home/mux/Desktop/AllFilesOf${fileExtension}.txt
I tried tee, sed and dd of, I also tried running it with bash -c or in sudo -i , nothing worked. Either i get an empty file or a Permission denied error.
I searched thoroughly and read many command manuals but I can't get it to work
The $() operator performs command substitution. When the overall command line is expanded, the command within the parentheses is executed, and the whole construct is replaced with the command's output. After all expansions are performed, the resulting line is executed as a command.
Consider, then, this simplified version of your command:
$(find /etc -regex ".*\.conf") >> /home/mux/Desktop/AllFilesOfconf.txt
On my system that will expand to a ginormous command of the form
/etc/rsyslog.conf /etc/pnm2ppa.conf ... /etc/updatedb.conf >> /home/mux/Desktop/AllFilesOfconf.txt
Note at this point that the redirection is separate from, and therefore independent of, the command in the command substitution. Expanding the command substitution therefore does not cause anything to be written to the target file.
But we're not done! That was just the expansion. Bash now tries to execute the result as a command. In particular, in the above example it tries to execute /etc/rsyslog.conf as a command, with all the other file names as arguments, and with output redirected as specified. But /etc/rsyslog.conf is not executable, so that will fail, producing a "permission denied" message. I'm sure you can extrapolate from there what effects different expansions would produce.
I don't think you mean to perform a command substitution at all, but rather just to run the command and redirect its output to the given file. That would simply be this:
sudo find $root -regex ".*\.${fileExtension}" >> /home/mux/Desktop/AllFilesOf${fileExtension}.txt
Update:
As #CharlesDuffy observed, the redirection in that case is performed with the permissions of the user / process running the script, just as it is in your original example. I have supposed that that is intentional and correct -- i.e. that the script is being run by user 'mux' or by another user that has access to mux's Desktop directory and to any existing file in it that the script might try to create or update. If that is not the case, and you need the redirection, too, to be privileged, then you can achieve it like so:
sudo -s <<END
find $root -regex ".*\.${fileExtension}" >> /home/mux/Desktop/AllFilesOf${fileExtension}.txt
END
That runs an interactive shell via sudo, with its input is redirected from the heredoc. The variable expansions are performed in the host shell from which sudo is executed. In this case the redirection is performed with the identity obtained via sudo, which affects access control, as well as ownership of the file if a new one is created. You could add a chown command if you don't want the output files to be owned by root.

bash script check if file exist and sent an information email

I worte a simple script to check if there are some files exist (endded with .txt) in the dirctoey older than 6 hours, after the check to send an email.
The scripte isnt working well and as expected, I ask you if there's some simpler and more powerful way to do it? Basically it just needs to check if file eneded with .txt exists and older than 6hours, if yes an email should be sent.
This is my script
#!/bin/bash
DATE=`date +%Y.%m.%d-%H.%M`
HOSTNAME='host'
BASEDIR=`/usr/local/se/work/jobs/`
LOGFILE=`/usr/local/se/work/jobs/logs/jobs.log`
VERTEILER="anyemail"
# Functions
#
# function check if the jobs are exists
'find ${BASEDIR} -name "*.txt" -nmin +354' 2>$1 >>$LOGFILE
#function mail
cat << EOF | mailx -s "Example ${HOSTNAME} jobs `date +%Y.%m.%d-%H.%M`" -a ${LOGFILE} ${VERTEILER}
Hi,
Please check the Jobs.
Details :
`ls -ltr /usr/local/se/work/jobs/`
------------ END ----------------------------------------
.
Thank you
To redirect STDERR to STDOUT use 2>&1, you are doing it wrong with 2>$1
Also, the correct parameter for find is -mmin not -nmin like you have in your code.
Further more you have syntax errors like here:
BASEDIR=`/usr/local/se/work/jobs/`
LOGFILE=`/usr/local/se/work/jobs/logs/jobs.log`
What you mean to type is:
BASEDIR='/usr/local/se/work/jobs/'
LOGFILE='/usr/local/se/work/jobs/logs/jobs.log'
When you use backticks bash tries to execute COMMAND, you are using it the right way here:
DATE=`date +%Y.%m.%d-%H.%M`
You are also not closing the heredoc <<EOF, you need EOF on the last line of the script.
And lose the '' surrounding the find command.
You should pay attention to what bash says, it should prompt lots of errors, try to run the script manually to see them if you are using this via cron.
Did you even try to run this script?
The grave accent (`) in BASEDIR and LOGFILE means the shell will try to evaluate them as commands which fails. You don't generally need quotes around strings in shell scripts, although it may be considered good practice to use double quotes (").
HOSTNAME=host
BASEDIR=/usr/local/se/work/jobs
LOGFILE=${LOGFILE}/logs/jobs.log
VERTEILER=anyemail
The switch to search files by minutes in called mmin and not nmin -- again that should have given you an error. And the math is wrong, if you want 6 hours then it's 6 * 60 = 360 minutes.
find ${BASEDIR} -name *.txt -mmin +360
You are redirecting stderr to the first input parameter. (2>$1) Are you expecting error output from this command, could you explain what is going on here?
And then you append that to LOGFILE which is in a directory that may not exist. mkdir -p is a good choice for creating folders in scripts because it creates parent directories when needed and won't complain if the folder already exists. So do something along the lines of
mkdir -p /usr/local/se/work/jobs/logs

How to ignore the ./ characters in the file location?

I run the following command to get the location of a .sh file and store it in a variable:
startupfile=`find . -name "startup.sh"`
The result of the above is:
./server1/bin/startup.sh
The next step is to run the startup.sh file.
When I try sh $startupfile, it fails to run the server.
Is there a way to ignore the ./ in the location value? Any other way to run the startup.sh script.
You should use:
startupfile=$(find . -name "startup.sh")
OR less preferred in BASH:
startupfile=`find . -name "startup.sh"`
This is called command substitution
UPDATE:: Post question edit:
To avoid running the script with full path you can place path of startup.sh in your PATH variable, so something like this in your ~/.bashrc:
export PATH=$PATH:~/server1/binripts
Then you can run:
startup.sh
From anywhere your home directory.
The ./ is harmless.
You have another problem. Did you change directory between the two commands? Is the "script" not actually a valid script?
The ./ is not the problem, it is simply a (superfluous) path to the current directory: ./file and ././././file are both valid paths to file in the current directory, the number of leading ./'s doesn't make any difference.
Looking at the source of your original question (before it was edited for better formatting), there were single quotes ' around the command: 'sh $startupfile'. If you had those in the actual command as well, that is your problem; the command should be: sh "$startupfile".
Other than that, it's hard to tell what the problem might be. Check that echo "$startupfile" shows the correct path and only the correct path. If it does, does sh ./server1/bin/startup.sh work? If not, what is the error?
To answer your specific questions:
Is there a way to ignore the ./ in the location value?
There are a couple of ways of stripping off leading directory names. The most efficient, though most obscure is:
startupfile=${startupfile##*/}
That removes the longest string on the left ending in '/'. The second way is a little more obvious, but involves creating a child process:
startupfile=$(basename "$startupfile")
Any other way to run the startup.sh script.
Yes, you could use the PATH environment variable, as #anubhava said. The only thing I would add is that you need execute access on the script; chmod u+x "$startupfile".
However, the implication of the name "startupfile" is that it is doing some sort of initialisation, in which case you might need to run it in the same process using the source or .(dot) command:
source "$startupfile"
You might still need the path in the filename, or update PATH.

Windows TYPE to Console recreated using Unix Shell scripting

We have simple Windows batch files that when an error occurs, an "ONCALL.bat" file is run to display support information that is maintained in a separate oncall.txt text file. This is our SOP.
ONCALL.BAT:
set scriptpath=%~dp0
TYPE "%scriptpath%oncall.txt"
I have zero experience with Unix and Shell scripts and I need to quickly provide a shell script equivalent to run in a Unix environment.
Could someone please provide me the .sh equivalent of this code?
Assuming that the help file and the script are in the same directory:
#!/bin/sh
SCRIPTPATH=`dirname "$0"`
cat "$SCRIPTPATH"/oncall.txt
$0 is the file path of the current script; the dirname command extracts the directory part of it. This way you can avoid using a hard-coded path for the help file within the script.
cat oncall.sh
#!/bin/bash
scriptpath=/path/to/scripts
cat ${scriptpath}/oncall.txt
After you create your file, it can't hurt to run
dos2unix oncall.sh
Just to be sure there are no windows Ctrl-M chars that will totally mystify you with the way they can screw up Unix script processing.
THEN
chmod 755 oncall.sh
To make the script executable.
confirm with
ls -l oncall.sh
You should see listing like
-rwxr-xr-x 1 userName grpname 5263 Nov 21 14:44 oncall.sh
Finally, call the script with a full or relative path, i.e.
./oncall.sh
OR
$PWD/oncall.sh
The first line is called the "shebang" line, and when your script is called, the OS reads the first line of the file, to find out what program to run to interpret the rest of the script file.
You may want/need to use as the first line "shebang" one of the following, but bash is a good guess
#!/bin/ksh
#!/bin/sh
#!/bin/ash
#!/bin/dash
#!/bin/zsh
OR you may worst case, your shell lives in a non-standard directory, then you'll have to spell that out, i.e.
#!/usr/bin/ksh
All shell support debugging arguments for trace and variable expansion like
#!/bin/ksh -vx
Or you can wrap just certain lines to turn debugginng on and off like
set -vx
cat ${scriptpath}/oncall.txt
set +vx
Given that
The ~dp special syntax between the % and the 0 basically says to expand the variable %0 to show the drive letter and path, which gives you the current directory containing the batch file!
I think /path/to/scripts is a reasonable substitute, scriptpath=$PWD would be a direct replacement, as there are no drive letters in Unix. The problem there, is that you either rely on unix PATH var to find your script or you cd /path/to/scripts and then run ./oncall.sh using the relative path./ to find the file without naving added a value to PATH.
IHTH.

Resources