Why find piped to xargs mv deleted my files? - linux

Today I experienced something unbelievable. My goal was to mv files newer than 7 days to another directory. The directory exist.
I used command:
find ./* -newermt $(date +%Y-%m-%d -d '7 day ago') -type f -print | xargs -I '{}' mv {} ../update_error_handled
Then, unbelievably the files were gone, I went to the folder used ls -lA and didn't found any files I moved. What happened? CentOS 7.0, there were no directory mount, original files missing, tried to grep -r "content" / - found nothing... .
So why it did behave that way?
Beforehand I launched
find ./* -newermt $(date +%Y-%m-%d -d '7 day ago') -type f -print it returned:
./file66.xml
./file67.xml
...etc.
It really do sucks to lose data in such a way.
To clarify: Directory existed before moving files. Directory does not contain my files I tried to move today, only older ones.

Did you create in advance a directory at ../update_error_handled?
If not, then all that you have left from the files will be the last one, which will be called ../update_error_handled.
In order to avoid such mistakes, I always make sure that the destination directory exists, by adding /. at the end of the destination directory name.
Unsafe approach:
$ rm -rf file dest_dir
$ touch file
$ ls -ld file dest_dir
ls: cannot access 'dest_dir': No such file or directory
-rw-rw-r--. 1 u u 0 Sep 28 11:53 file
$ mv file dest_dir
$ ls -ld file dest_dir
ls: cannot access 'file': No such file or directory
-rw-rw-r--. 1 u u 0 Sep 28 11:53 dest_dir
Using the unsafe approach, file was renamed to a file named dest_dir.
Safe approach:
$ rm -rf file dest_dir
$ touch file
$ ls -ld file dest_dir
ls: cannot access 'dest_dir': No such file or directory
-rw-rw-r--. 1 u u 0 Sep 28 11:54 file
$ mv file dest_dir/.
mv: cannot move 'file' to 'dest_dir/.': No such file or directory
$ ls -ld file dest_dir
ls: cannot access 'dest_dir': No such file or directory
-rw-rw-r--. 1 u u 0 Sep 28 11:54 file
Using the safe approach, the mv command failed and the file file remained intact.

Related

How to tar a branch of file tree?

I currently have some files and directories at this path:
/var/tmp/mydir/
I want to tar the whole path, excluding any other content in 'var' and 'tmp'.
Example:
$ ls /var
tmp
dir1 *(exclude)*
file1 *(exclude)*
$ ls /var/tmp
mydir
dir2 *(exclude)*
file2 *(exclude)*
$ ls /var/tmp/mydir
tarme1
tarme2
tarme3
In this case, I want to tar the directory tree /var/tmp/mydir and the content of 'mydir'.
Use tar -cf <archive_name>.tar /var/tmp/mydir which will give you what you need.
Use man tar to get more help (should be quite easy to understand).
If you want to modify your path some other way consider using -C switch. From man:
-C, --directory DIR
change to directory DIR
Do
tar -c --recursion --file backup.tar tmp/mydir
and
tar -tvf backup.tar
gives me :
drwxrwxr-x ssam/ssam 0 2016-05-02 12:02 tmp/mydir/
-rw-rw-r-- ssam/ssam 0 2016-05-02 12:02 tmp/mydir/tarme3
-rw-rw-r-- ssam/ssam 0 2016-05-02 12:02 tmp/mydir/tarme1
-rw-rw-r-- ssam/ssam 0 2016-05-02 12:02 tmp/mydir/tarme2
which is what you need. You can extract/restore it using
tar -xf backup.tar -C /var
Remember this will overwrite the files in mydir

Move multiple files with unique name to new folder and append to file name

I have about 2000 files in a folder.
All the files contain the string test in the name.
What I need to do is move all those files ~1250 to a folder called trash within the same directory and append _scrap to the end of each file.
mv *test* trash/
What I want is something like this:
[root#server] ls
test1.txt test2.txt test3.txt trash video1.txt video2.txt video3.txt
[root#server] mv *test* trash/*_scrap
[root#server] ls
trash vidoe1.txt video2.txt video3.txt
[root#server] ls trash/
test1.txt_scrap test2.txt_scrap test3.txt_scrap
I can move all files, however I cannot figure out how to append the _scrap to the end.
As I have to do this on a number of machines, a one liner would be preferable over a small script.
$ touch test1.txt test2.txt test3.txt vidoe1.txt vidoe2.txt vidoe3.txt
$ mkdir trash
$ for file in *test*; do mv "$file" "trash/${file}_scrap"; done
$ ls
trash vidoe1.txt vidoe2.txt vidoe3.txt
$ ls trash
test1.txt_scrap test2.txt_scrap test3.txt_scrap
$
You could also use xargs
$ ls *test* | xargs -t -I{} mv {} trash/{}_scrap
mv test1.txt trash/test1.txt_scrap
mv test2.txt trash/test2.txt_scrap
mv test3.txt trash/test3.txt_scrap
$
You could use find
$ find . -name '*test*' -maxdepth 1 -exec mv {} trash/{}_scrap \;
You can use rename to avoid shell for loops. It's a perl script but it comes installed with many common distros (including Ubuntu 14):
$ mv *test* trash/
$ rename 's/$/_scrap/g' trash/*
$ ls trash/
test1.txt_scrap test3.txt_scrap test2.txt_scrap

How can I get the owner of every file in a directory in Linux?

I need to check if root is the owner of every file in a particular directory. I can do
stat --format=%u /directory/name/here
to get the owner of the directory itself, but not the files in it.
My other idea was to do
ls -lL | grep "^-\|^d" | cut -d ' ' -f 2
but that doesn't work if the last byte in the permissions is a space and not a '.'.
This is also CentOS if that matters.
you can use find:
find /tmp -type f -printf '%u\n' | sort -u
lightdm
root
tiago
If you need UID in numeric form, like using stat:
find /tmp -type f -printf '%U\n' | sort -u
0
1000
104
You're asking two different questions.
I need to check if root is the owner of every file in a particular directory
To find any files that are not owned by root, you can do:
find /yourdir ! -user root
If it returns any filenames at all, then root is not the owner of every file in the particular directory.
How can I get the owner of every file in a directory in Linux?
To print every file in the directory with username:
find /yourdir -printf '%u %p\n'
And if the final step would be to chown the files not owned by root, you can simply do chown -R root /yourdir, since there's no harm in chowning root's files to root.
Try
find /your/dir/ -type f -exec stat --format='%u %n' '{}' \;
I added %n to display the file name.
Read find(1) for more info about find .... You may want -max_depth 1 to avoid going deeply in /your/dir/
for F in /directory/*; do stat --format='%u' "$F"; done
And optionally add dotglob option to match files beginning with . as well:
shopt -s dotglob
for F in /directory/*; do stat --format='%u' "$F"; done
* --format is equivalent to -c.

Find command in linux

What does the following means ?
find myDirectory -name myFile -exec ls \-ln {} \;
I've looked here but didn't understand exactly
-exec command True if the executed command returns a zero value as exit status. The end of command must be punctuated by an escaped semicolon. A command argument {} is replaced by the current path name.
This part -exec ls \-ln {} \; is not clear to me .
Regards
That means: find all files with a name myFile in the current directory and all its subdirectories and for every file that was found run ls -ln with the name of the file.
For example:
$ mkdir a
$ touch myFile a/myFile
$ find -name myFile -exec ls -ln {} \;
-rw-r--r-- 1 1000 1000 0 Jun 17 13:07 ./myFile
-rw-r--r-- 1 1000 1000 0 Jun 17 13:07 ./a/myFile
In this case find will run ls twice:
ls -ln ./myFile
ls -ln ./a/myFile
Every time it will expand {} as the fullname of the found file.
Also I must add that you need the backslash before -ln in this case. Yes, you can use it, but it is absolutely useless here.
find myDirectory -name myFile -exec ls \-ln {} \;
It says find myFile in directory myDirectory and once all the files are found then execute the file listing command, that is in linix ls with command options -l and -n on the files found.
So, ultimately you will get all the myFiles accompanied with ls command result.

Find all writable files in the current directory

I want to quickly identify all writable files in the directory. What is the quick way to do it?
find -type f -maxdepth 1 -writable
The -writable option will find files that are writable by the current user. If you'd like to find files that are writable by anyone (or even other combinations), you can use the -perm option:
find -maxdepth 1 -type f -perm /222
This will find files that are writable by their owner (whoever that may be):
find -maxdepth 1 -type f -perm /200
Various characters can be used to control the meaning of the mode argument:
/ - any permission bit
- - all bits (-222 would mean all - user, group and other)
no prefix - exact specification (222 would mean no permssions other than write)
to find writable files regardless of owner, group or others, you can check the w flag in the file permission column of ls.
ls -l | awk '$1 ~ /^.*w.*/'
$1 is the first field, (ie the permission block of ls -l) , the regular expression just say find the letter "w" in field one. that's all.
if you want to find owner write permission
ls -l | awk '$1 ~ /^..w/'
if you want to find group write permission
ls -l | awk '$1 ~ /^.....w/'
if you want to find others write permission
ls -l | awk '$1 ~ /w.$/'
-f will test for a file
-w will test whether it's writeable
Example:
$ for f in *; do [ -f $f ] && [ -w $f ] && echo $f; done
If you are in shell use
find . -maxdepth 1 -type f -writable
see man find
You will find you get better answers for this type of question on superuser.com or serverfault.com
If you are writing code not just using shell you may be interested in the access(2) system call.
This question has already been asked on serverfault
EDIT: #ghostdog74 asked if you removed write permissions for this file if this would still find the file. The answer, no this only finds files that are writable.
dwaters#eirene ~/temp
$ cd temp
dwaters#eirene ~/temp/temp
$ ls
dwaters#eirene ~/temp/temp
$ touch newfile
dwaters#eirene ~/temp/temp
$ ls -alph
total 0
drwxr-xr-x+ 2 dwaters Domain Users 0 Mar 22 13:27 ./
drwxrwxrwx+ 3 dwaters Domain Users 0 Mar 22 13:26 ../
-rw-r--r-- 1 dwaters Domain Users 0 Mar 22 13:27 newfile
dwaters#eirene ~/temp/temp
$ find . -maxdepth 1 -type f -writable
./newfile
dwaters#eirene ~/temp/temp
$ chmod 000 newfile
dwaters#eirene ~/temp/temp
$ ls -alph
total 0
drwxr-xr-x+ 2 dwaters Domain Users 0 Mar 22 13:27 ./
drwxrwxrwx+ 3 dwaters Domain Users 0 Mar 22 13:26 ../
---------- 1 dwaters Domain Users 0 Mar 22 13:27 newfile
dwaters#eirene ~/temp/temp
$ find . -maxdepth 1 -type f -writable
dwaters#eirene ~/temp/temp
for var in `ls`
do
if [ -f $var -a -w $var ]
then
echo "$var having write permission";
else
echo "$var not having write permission";
fi
done
The problem with find -writable is that it's not portable and it's not easy to emulate correctly with portable find operators. If your version of find doesn't have it, you can use touch to check if the file can be written to, using -r to make sure you (almost) don't modify the file:
find . -type f | while read f; do touch -r "$f" "$f" && echo "File $f is writable"; done
The -r option for touch is in POSIX, so it can be considered portable. Of course, this will be much less efficient than find -writable.
Note that touch -r will update each file's ctime (time of last change to its meta-data), but one rarely cares about ctime anyway.
Find files writeable by owner:
find ./ -perm /u+w
Find files writeable by group:
find ./ -perm /g+w
Find files writeable by anyone:
find ./ -perm /o+w
Find files with defined permission:
find ./ -type -d -perm 0777
find ./ -type -d -perm 0755
find ./ -type -f -perm 0666
find ./ -type -f -perm 0644
Disable recursive with:
-maxdepth 1
stat -c "%A->%n" *| sed -n '/^.*w.*/p'
I know this a very old thread, however...
The below command helped me: find . -type f -perm /+w
You can use -maxdepth based on how many levels below directory you want to search.
I am using Linux 2.6.18-371.4.1.el5.
If you want to find all files that are writable by apache etal then you can do this:
sudo su www-data
find . -writable 2>/dev/null
Replace www-data with nobody or apache or whatever your web user is.

Resources