LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - General (http://www.linuxquestions.org/questions/linux-general-1/)
-   -   Shell script to get name of file, delete original file, rename blank file (http://www.linuxquestions.org/questions/linux-general-1/shell-script-to-get-name-of-file-delete-original-file-rename-blank-file-4175427010/)

chrisgti 09-13-2012 03:49 AM

Shell script to get name of file, delete original file, rename blank file
 
Hi,

I've had a request to help out someone accomplish a task involving a load of files in a directory.

Essentially, what I need to achieve is along the lines of the following, for each file in the directory:

- Get the file name & assign to var
- delete the original file
- take a copy of a placeholder file (which we will just call placeholder.txt)and add .tmp extension
- rename the copy of the placeholder.txt.tmp file to the original file name based on the var

I have a few ideas but I am not sure where best to start and I am tripping up with my script so far - any pointers would be greatly appreciated :)

Thanks

segmentation_fault 09-13-2012 04:45 AM

Why don't you just overwrite the original file (using cp or even cat)? Is there a reason you want to delete it first?

chrisgti 09-13-2012 09:45 AM

Overwriting would also be an option so long as the file name stays the same - say for instance we have files 1.txt 2.txt ...... 10.txt

I need to loop through the directory, get each file name, and basically replace the file with that name with a placeholder file with the same name

It's a bit of an unusual request though admittedly

I have been trying to do some basic scripting but I am tripping up with the logic needed

I suppose another way would be to go through the directory, get all the file names (assign to array?), and then for each file do a cp placeholder.txt filename.txt, and then as you say...set it to overwrite the original

I would need to get the filename without the extension though, otherwise it would end up being file.txt.txt i guess..

chrisgti 09-13-2012 09:57 AM

Had a bit of a brainwave...and realise it's almost going to be as simple as a 1 liner:..my base for testing is now:

Code:

for i in $(ls) '*.txt'; do cp placeholder.txt $i; done
Sometimes it's just nice to be prompted to think about things a little differently :)

I will try that!

segmentation_fault 09-13-2012 10:19 AM

I recently read a post here on LQ about not using ls in such scripts. Instead use something like
Code:

for filename in *; do <something> done
Check this why.

catkin 09-13-2012 10:45 AM

Safer to quote the $i in case its value contains something bash will substitute:
Code:

for i in *.txt; do cp placeholder.txt "$i"; done

chrisgti 09-13-2012 10:53 AM

Thanks for that article, I must admit I did not know that, and I will indeed follow your suggestion

However I am very close with the other way and I just want to fix it to understand where I am going wrong at the last hurdle:

I now have:

Code:

for i in $(ls | grep txt); do \cp ../placeholder.txt "$i"; done
And this works just fine, when I open a a TXT with the original file name, I can see that the new placeholder content is now there. EXCEPT for TXT files with spaces in the file names...and I then just see several files. For example. TXT WITH SPACES.TXT ends up looking like this in an ls:

TXT
SPACES.TXT
WITH

and also the original TXT WITH SPACES.txt

I have tried '$i' and "$i" - alas, no difference (though it does throw errors with just 'i', "i" works but the result is the same as above)

chrisgti 09-13-2012 11:00 AM

Quote:

Originally Posted by catkin (Post 4779469)
Safer to quote the $i in case its value contains something bash will substitute:
Code:

for i in *.txt; do cp placeholder.txt "$i"; done

Ah ha! That's got it, even with the spaces. Oddly I was tring for i in *.txt earlier today and I was getting errors that suggested it was looking inside the files, but I was obviously doing something wrong.

I am still curious to know then why when I use "$i" in the other command it doesn't work.

But thanks for the assistance, this has been way simpler than I first thought..and I learned something. Win, win!

suicidaleggroll 09-13-2012 11:14 AM

Quote:

Originally Posted by chrisgti (Post 4779474)
Thanks for that article, I must admit I did not know that, and I will indeed follow your suggestion

However I am very close with the other way and I just want to fix it to understand where I am going wrong at the last hurdle:

I now have:

Code:

for i in $(ls | grep txt); do \cp ../placeholder.txt "$i"; done
And this works just fine, when I open a a TXT with the original file name, I can see that the new placeholder content is now there. EXCEPT for TXT files with spaces in the file names...and I then just see several files. For example. TXT WITH SPACES.TXT ends up looking like this in an ls:

TXT
SPACES.TXT
WITH

and also the original TXT WITH SPACES.txt

I have tried '$i' and "$i" - alas, no difference (though it does throw errors with just 'i', "i" works but the result is the same as above)

It's because the for is splitting on spaces, your problem is the for i in $(ls | grep txt). If you had three files: a.txt, b.txt, and "c d.txt" (with a space), your "ls | grep txt" would spit out the single string "a.txt b.txt c d.txt". The for would then split on spaces, giving you "a.txt", "b.txt", "c", and "d.txt". This is why you want to do the for over *.txt directly, without throwing an intermediate ls in the middle. When you do "for i in *.txt" it will handle the spaces correctly. "for i in $(ls *.txt)" will not.

chrisgti 09-14-2012 04:42 AM

That all makes sense, thankyou :)

I've realised there is a final requirement - to make it work recursively

so I modified the one liner a bit after a bit of Googling:

Code:

#!/bin/bash
FILES=$(find folder_name -type f -name '*.txt')
for i in $FILES;
do
\cp placeholder.txt "$i";
done

This doesn't work with spaces either, so I tried

Code:

FILES="$(find folder_name -type f -name '*.txt')"
But apparently didn't make any difference...so, what am I missing?

Thanks :)

segmentation_fault 09-14-2012 05:22 AM

Maybe you should try something like
Code:

for file in *.txt; do cp placeholder.txt "$file"; done
If you want to use "find" for that check the "exec" part of find command and don't combine it with "for".

catkin 09-15-2012 03:49 AM

Quote:

Originally Posted by chrisgti (Post 4780060)
Code:

FILES="$(find folder_name -type f -name '*.txt')"
But apparently didn't make any difference...so, what am I missing?

It is the same issue. FILES is set to the output of find including whitespace. If you then use $FILES unquoted, bash turns it into a list of whitespace-separated words separated by spaces.

The classic robust idiom for looping over file names is:
Code:

while IFS= read -r -d '' file
do
  Do something with "$file"
done < <(find <whatever> -print0)

This search lists several examples of the above code.
.
This LQ post explains how it works.


All times are GMT -5. The time now is 03:37 AM.