Create a new empty file from linux command line with same permissions and ownership? - linux

I need to create a new empty file with the same permissions and ownership (group and user) as the source, kind of like how cp -p FILE1 FILE1.bak works but without actually copying the contents.
I know I can empty out the contents later on, but that seems wasteful.
I cant use a script - the solution must run from the command line directly.

touch newfile
chmod `stat -c %a originalfile` newfile
chown `stat -c %U originalfile`:`stat -c %G originalfile` newfile

Use
touch newfile
chmod --reference=oldfile newfile
chown --reference=oldfile newfile

cp --attributes-only --preserve=ownership

Use the touch command.
http://www.computerhope.com/unix/utouch.htm

Related

How to check if "s" permission bit is set on Linux shell? or Perl?

I am writing some scripts to check if the "s" permission bit is set for a particular file.
For example - permissions for my file are as follows-
drwxr-s---
How can I check in bash script or a perl script if that s bit is set or not?
If you're using perl, then have a look at perldoc:
-u File has setuid bit set.
-g File has setgid bit set.
-k File has sticky bit set.
So something like:
if (-u $filename) { ... }
non-perl options
Using stat
#!/bin/bash
check_file="/tmp/foo.bar";
touch "$check_file";
chmod g+s "$check_file";
if stat -L -c "%A" "$check_file" | cut -c7 | grep -E '^S$' > /dev/null; then
echo "File $check_file has setgid."
fi
Explanation:
Use stat to print the file permissions.
We know the group-execute permission is character number 7 so we extract that with cut
We use grep to check if the result is S (indicated setgid) and if so we do whatever we want with that file that has setgid.
Using find
I have found (hah hah) that find is quite useful for the purpose of finding stuff based on permissions.
find . -perm -g=s -exec echo chmod g-s "{}" \;
Finds all files/directories with setgid and unsets it.

How to touch a file and mkdir if needed in one line

I need to touch a file with an absolute file name such as: /opt/test/test.txt, but I'm not sure if there is /opt/test existed on the system. So the code should similar with this:
if (-d '/opt/test') {
touch '/opt/test/test.txt';
} else {
mkdir -p '/opt/test';
touch '/opt/test/test.txt'
}
Is there any better way to simplify the code? I hope there is some system commands that can do the same job with only one line.
mkdir B && touch B/myfile.txt
Alternatively, create a function:
mkfile() {
mkdir -p $( dirname "$1") && touch "$1"
}
Execute it with 1 arguments: filepath. Saying:
mkfile B/C/D/myfile.txt
would create the file myfile.txt in the directory B/C/D.
In a shell script, you can simply do:
mkdir -p /opt/test && touch /opt/test/test.txt
mkdir -p will not fail (and won't do anything) if the directory already exists.
In perl, use make_path from the File::Path module, then create the file however you want. make_path also doesn't do anything if the directory exists already, so no need to check yourself.
In perl, using one of my favorite module: Path::Tiny.
path("/opt/test/test.txt")->touchpath;
From the doc:
Combines mkpath and touch. Creates the parent directory if it doesn't
exist, before touching the file.
I like typing very little, so I put this command into a named fn in my .profile, but I used this formulation for years before I did it:
mkdir -p dirname/sub/dir && touch $_/filename.ext
The variable $_ stores the last argument to the previous command. Pretty handy to know about overall.
I defined a touchp in my ~/.bash_aliases:
function touchp() {
/bin/mkdir -p "$(dirname "$1")/" && /usr/bin/touch "$1"
}
It silently creates the structure above the file if not present, and is perfectly safe to use when passed a single filename without any directory in front of it.
Perl from command line,
perl -MFile::Basename -MFile::Path=make_path -e'
make_path(dirname($_)), open(F, ">>", $_) for pop;
' /opt/test/test.txt
I have this shell function in my .zshalias file:
function touch-safe {
for f in "$#"; do
[ -d $f:h ] || mkdir -p $f:h && command touch $f
done
}
alias touch=touch-safe
If either the test or the mkdir command fail, no touch command is invoked.
Bring Python to command line.
i.e. Use pyp
cat filepaths.txt | pyp "'mkdir -p '+s[0:-1]|s+'; touch '+o" | sh
The Pyed Piper", or pyp, is a linux command line text manipulation tool similar to awk or sed, but which uses standard python string and list methods as well as custom functions evolved to generate fast results in an intense production environment.

mv all files except their file name ends with .zip

I'm looking for a Linux command to move files from a directory to another, but only if their file name doesn't end with .zip.
Is their a command like: mv ~/Folder1/!*.zip ~/Folder2/?
Try this:
mv $(ls ~/Folder1/ |grep -v "zip$" ) ~/Folder2/
If your shell is bash and the option extglob is enabled, you can do it like this too
mv ~/Folder1/!(*.zip) ~/Folder2/
Try this command:
mv ~/Folder1/!(*.zip) ~/Folder2/

Cat changes owner?

I have to edit a file owned by root via ssh. I add a entry in the file, preserve the first 9 lines and reorder the rest to a temporary file. I know that > overwrittes what's in the file (and that's what i want) but I need to preserve the root as owner of file. How can I do this? Thanks!
#!/bin/bash
user=""
echo "User:"
read user
ssh xxxx#xxxx "
sed -i '\$a$user' file;
(head -n 9 file ; tail -n +10 file | sort) > temp;
cat temp > file;
rm -f temp
"
It's not cat that's changing the owner, it's sed. When you use sed -i, it does something like:
mv file file.bak
sed '\$a$user' file.bak > file
rm file.bak
As you can see, this creates a new file with the original file's name, and it's owned by the user that creates the file.
If you want to avoid this, make a copy of the original file instead of using the -i option.
cp file /tmp/file.$$
sed '\$a$user' /tmp/file.$$ > file
rm /tmp/file.$$
Or you could just put sed into your pipeline:
sed '\$a$user' file | head -n 9 file ; tail -n +10 file | sort > temp
cat temp > file
rm temp
Been a while since I wrote in BASH but i think a starting point would be
chown root $file // if you have a variable with the file name in
or
chown root thefile.txt //if you want it hard coded;
another variable in the equation would be who has ownership of the application cat? i think whoever is the owner of the running application, thats how the ownership of the files its out put is decided
maybe you could also try
$ sudo cat temp > file
because the session would then belong to the root and therefore the ouput would belong to the root???
I take it that the user who logs in cannot become root?
THen your best bet is to use dd
dd if=tmpfile of=outfile
of course, do all the ordering, seding, awking and greping on your tmp file. dd in this usage is equivalent > without creating a new file
#!/bin/bash
user=""
echo "User:"
read user
ssh xxxx#xxxx "
sed -i '\$a$user' file;
(head -n 9 file ; tail -n +10 file | sort) > temp;
sudo mv temp file;
sudo chown root file
"
This will work better if on the xxxx machine the xxxx user you're logging in as has password-less access to sudo. You can do this by having this entry in your /etc/sudoers file there:
xxxx ALL=NOPASSWD: ALL

Linux script for copying files from multiple windows machines

Having a issue trying to make a bash script that will read ip address and usernames from a file that mount a connection to that share on windows and then copy ano file types into a new folder called the users name.
At the moment it doesn't quite work, it makes hundreds of folders called *.ano if it can not find the windows share.
Help please
Text file:
192.168.0.2 user1
192.168.0.3 user2
bash script:
USER='/home/user/user.ip'
IPADDY=$(grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' $USER)
USERNAME=$(awk '{ print $NF }' $USER)
for i in $IPADDY $USERNAME
do
mkdir /home/user/Documents/$USERNAME
mount -t smbfs //$IPADDY/$USERNAME /home/user/$USERNAME
rsync -va /home/user/$USERNAME/*.ano /home/user/Documents/$USERNAME/*.ano
done
Hi all thanks for such a quick reply, I have change the code as follow but still get multiple files have I done something wrong here
USER='/home/user/user.ip'
IPADDY=$(grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' $USER)
USERNAME=$(awk '{ print $NF }' $USER)
while read IPADDY USERNAME; do
mkdir /home/user/Documents/$USERNAME
mount -t smbfs //$IPADDY/$USERNAME /home/user/$USERNAME
rsync -va /home/user/$USERNAME/*.ano /home/user/Documents/$USERNAME/
done < $USER
The problem is in the for command. In your script, i iterates over the contents of $IPADDY, then it iterates over the contents of $USERNAME. Meanwhile, $USERNAME inside the loop gets expanded to user1 user2, resulting in:
mkdir /home/user/Documents/user1 user2
The mount line becomes:
mount -t smbfs //192.168.0.2 192.168.0.3/user1 user2 /home/user/user1 user2
And so on.
Rather, loop over the file itself:
while read IPADDY USERNAME; do
#awesome things here based on $IPADDY and $USERNAME
done < $USER
You might want to add [[ -z $IPADDY ]] && continue to skip over any possible blank lines in the file.
One problem is that you use a wildcard (*) for the destination files. But those files don't exist - therefore /home/user/Documents/$USERNAME/*.ano cannot match and rsync will create a file *.log.
Better do:
rsync -va /home/user/$USERNAME/*.ano /home/user/Documents/$USERNAME/

Resources