Showing posts with label files. Show all posts
Showing posts with label files. Show all posts

Saturday, March 13, 2021

Processing Large Quantities of Files with Find/Exec

Photo by Markus Winkler from Pexels

 

I've always found the find command to be incredibly useful, but using with the exec command powerful but frustrating and confusing.  Often, patience runs thin and rather than take the time to learn how to effectively use find/exec for complex problems I bunt, returning to creating a tailor-made one-off bash script.  In the end, mission accomplished, but I always feel disappointed to have to revert to a custom bash script when I know in my heart-of-hearts its accomplishable quickly if I only knew how to do it.

Today is the day, I'm gonna spend some time to better understand how to use find/exec for some repeatedly necessary types of problems.

Let's start with an easy case, one that's less common, but really easy to accomplish.  We'll build from there.

Copying Files To New File Name (Prepending/Appending)

Let's say you have a list of files that you want to rename by pre-pending, or appending a substring.  For instance, say you have a hierarchy of directories with image files that you wish to copy to a *.backup filename;

$ find . -name "*.jpg" -exec cp {} {}.backup \;

The above command will find all *.jpg named files, for each file execute 'cp <filename> <filename>.backup'.  In other words, when finding image01.jpg the exec command would be cp image01.jpg image01.jpg.backup.  This would be done for every encountered file that satisfies the regex.

Prepending a string in a similar manner could be done by:

$ find . -name "*.jpg" -exec cp {} backup-{} \;

In this case image01.jpg would be copied to backup-image01.jpg.

Simple, fast, but not particularly useful if you're particular about the destination file names.

Replacing File Extension 

A bit more practical scenario is to want to change file extensions.  For example, say you really prefer *.jpg but have a series of files named *.jpeg.

This one is a bit trickier, takes a little more expertise, but can readily be accomplished and understood with a bit of time.

$ find . -name "*.jpeg" -exec sh -c 'mv "$0" "${0%.jpeg}.jpg"' {} \;

The simple filename substitution (e.g. {}) just doesn't cut it like the previous example because we wish to manipulate the filename.  So, we inline a shell command, one that is capable of using the incoming file name as is (e.g. $0) and able to manipulate it (changing .jpeg to .jpg).  That's the brief, lets dig a bit into it to better understand what's going on.

Incoming filenames sent to the shell script will be called *.jpeg (guaranteed by the find regex).  The filename comes in as a parameter (e.g. $0) to the shell script so the first 1/2 of the move command could be 'mv file01.jpeg ...'.  It may be worth pointing out, those that author shell commands may be familiar with $0 being the script name and the first argument be $1, but for an inline shell script, the first argument will be $0 as we are using it.

How about the seconds 1/2 of the shell command; while it looks like Snoopy dropping curse words it genuinely is meaningful.


The "${0%.jpeg}.jpg" is comprised of two parts; the first part ${0%.jpeg} is a variable pattern substitution;

${var%Pattern} Remove from $var the shortest part of $Pattern that matches the back end of $var. 

refer to this for details: https://tldp.org/LDP/abs/html/parameter-substitution.html

Simply put, take $0 (the filename) and grab everything up to .jpeg, image01.jpeg would be expanded to image01.

The second 1/2 of the expression simply re-adds .jpg, so the whole expression of image01.jpeg would be image01.jpg.  With the existing and new file names now available, pairing them with a mv command and you're in business.

Removal of Spaces in File Names

 Ugh, I'd rather step in dog shit barefoot than have spaces in my file names.  I know, it's an irrational hatred but there it is.  Filenames are the bane of scripting, while they can be addressed, a simple 3 line shell script quickly becomes immensely more complex when dealing with filenames w/spaces.  But, like bedbugs, any exposure to the outside world likely will bring them into your system.  So, you need to be prepared to either live with them or a quick means to remove them from the filenames.  My latest headache was downloading a series of video files from a MOOC, resulting in files of the form 'index 1.mp4'.  So, we will extend on our above example, but utilize bash (rather than sh) to gain some substitution features.  The "${0/ /_}" (a space between the '/' pairs) means replace all instances of spaces with '_'

        $ find . -name "*.html" -exec bash -c 'mv "${0/ /_}"' {} \;

Massive Media Conversion in One Command

While we've been focusing on shell scripts using 'cp' or 'mv' commands, we aren't limited to easy commands, let's say we wished to convert a hierarchical folder structure of AVI files that we want to reencode as MP4 files.

$ find . -name "*.avi" -exec sh -c 'ffmpeg -i "$0" -acodec copy "${0%.avi}.mp4"' {} \;

Cut that puppy loose on your computer and come back to a newly created list of MP4 files.

 

Hope this helps some of you.  I feel I understand the use of exec better having worked through this.  Cheers.

Dealing With Filenames Containing Spaces

Photo by XXSS IS BACK from Pexels

 

Since the Internet is used by a variety of users, operating systems and naming conventions it's not uncommon to get filenames that contain spaces.  Trouble is, this can wreak havoc with simple scripts that don't anticipate filenames with spaces.


for example;
With a folder containing files like this:
lipeltgm@kaylee:/var/tmp$ ls *ts
Nokomis Track - Minnebar15 - Oct 10th – Crowdcast-TohCuy6dhE00Y02iSoGYDo75LAQVfEmKmk (1).ts
Nokomis Track - Minnebar15 - Oct 10th – Crowdcast-TohCuy6dhE00Y02iSoGYDo75LAQVfEmKmk.ts
Nokomis Track - Minnebar15 - Oct 13th – Crowdcast-Qcgrm8WtcFKjmHYGs00Bg01Hy2HkkmmZYs.ts
Nokomis Track - Minnebar15 - Oct 6th – Crowdcast-OmLjaiEpTLslbQ9GHLmKITTSagJ00COuy.ts
Nokomis Track - Minnebar15 - Oct 8th – Crowdcast-JyWZ7lzwvH5MZSuGvp2uE49DVpeCVO3r.ts
Phalen Track Backup - Minnebar15 - Oct 13th – Crowdcast-dozLLcdmIlG6Bqdx2i99eNO8giYjpYsN.ts
Phalen Track - Minnebar15 - Oct 10th – Crowdcast-lzz9BX00V8cswvC2csSvecrjV00w1o80201G.ts
Phalen Track - Minnebar15 - Oct 13th – Crowdcast-vod_master.ts
Phalen Track - Minnebar15 - Oct 6th – Crowdcast-m7ItulACMOv6a6jhQuswa1nwseStEsfj.ts
Phalen Track - Minnebar15 - Oct 8th – Crowdcast-3xMlXLl8kVgTBVmbw602c78acyLDJpLcM.ts

A simple script, as follows, will interpret each entry as space/eol seperated.  Yes, you can address it by changing the delimiter, but another option is to simply rename the files to something more expected.

$ cat /tmp/go
for file in `find . -name "*.ts"`; do
echo $file
done

 The following find command will locate files with spaces in their names, changing the spaces to underscores ('_') to resemble something a bit easier to work with.

$ find . -type f -name "* *" | while read file; do mv "$file" ${file// /_}; done

Hope this helps.

Saturday, July 6, 2019

System Forensics -- Locating Relevant Log Files

Every once in a while you'll get an assignment to figure out something about a system that no one knows anything about.  Someone will point you toward an executable, like a system daemon and wish you on your way with a hearty 'good luck'.  Now what?

A good start would be to take a peek at any relevant log files, but how do you know where to look?  An intellectual game of hide and seek begins.

Perhaps the process status will provide you with a clue; a log directory or log file may be specified in the command line arguments.  If not, what's next?

You can continue by seeing if they use the typical system log conventions, examining the /var/log/ log files for traces of relevant events.  If that path proved unfruitful....then what?

The 'list open files' utility can be a solid next step.  The 'lsof' utility, when provided a process id, will show all files the process is accessing.  A hard-coded log file which may be difficult to locate in the source code (if available) is easily found with this particular utility.  Sorting through the plethora of open file list can take some doing, it's pretty exhaustive and a wealth of information, showing shared libraries the process is using, open sockets, open files,..... If the executable is writing to a log file, it'll be in the lsof file listing.  This comes in extremely handy in finding otherwise elusive log files.

Let's do one quick example; say you know nothing about the apache service but want to know what log files it's using.




root@river:~# ps -aef | grep apache
root      1467     1  0 May25 ?        00:00:00 /usr/sbin/apache2 -k start
www-data  9948  1467  0 20:59 ?        00:00:00 /usr/sbin/apache2 -k start
www-data  9949  1467  0 20:59 ?        00:00:00 /usr/sbin/apache2 -k start


We'll focus on the process 1467.




root@river:~# lsof -p 1467
lsof: WARNING: can't stat() fuse.gvfsd-fuse file system /run/user/1000/gvfs
      Output information may be incomplete.
COMMAND  PID USER   FD   TYPE DEVICE SIZE/OFF      NODE NAME
apache2 1467 root  cwd    DIR    8,1     4096         2 /
apache2 1467 root  rtd    DIR    8,1     4096         2 /
apache2 1467 root  txt    REG    8,1   662560 114558879 /usr/sbin/apache2
apache2 1467 root  mem    REG    8,1    47600 120590386 /lib/x86_64-linux-gnu/libnss_files-2.23.so
apache2 1467 root  mem    REG    8,1    47648 120590377 /lib/x86_64-linux-gnu/libnss_nis-2.23.so
apache2 1467 root  mem    REG    8,1    93128 120590384 /lib/x86_64-linux-gnu/libnsl-2.23.so
apache2 1467 root  mem    REG    8,1    35688 120590392 /lib/x86_64-linux-gnu/libnss_compat-2.23.so
apache2 1467 root  mem    REG    8,1    22536 114821038 /usr/lib/apache2/modules/mod_status.so
apache2 1467 root  mem    REG    8,1    14344 114833078 /usr/lib/apache2/modules/mod_setenvif.so
apache2 1467 root  mem    REG    8,1    34832 114833033 /usr/lib/apache2/modules/mod_negotiation.so
apache2 1467 root  mem    REG    8,1    63504 114833026 /usr/lib/apache2/modules/mod_mpm_event.so
apache2 1467 root  mem    REG    8,1    18440 114833076 /usr/lib/apache2/modules/mod_mime.so
apache2 1467 root  mem    REG    8,1    18440 114821057 /usr/lib/apache2/modules/mod_filter.so
apache2 1467 root  mem    REG    8,1    10248 114833082 /usr/lib/apache2/modules/mod_env.so
apache2 1467 root  mem    REG    8,1    14344 114821225 /usr/lib/apache2/modules/mod_dir.so
apache2 1467 root  mem    REG    8,1   104864 120586429 /lib/x86_64-linux-gnu/libz.so.1.2.8
apache2 1467 root  mem    REG    8,1    34824 114833080 /usr/lib/apache2/modules/mod_deflate.so
apache2 1467 root  mem    REG    8,1    38928 114833054 /usr/lib/apache2/modules/mod_autoindex.so
apache2 1467 root  mem    REG    8,1    10256 114832998 /usr/lib/apache2/modules/mod_authz_user.so
apache2 1467 root  mem    REG    8,1    10256 114821227 /usr/lib/apache2/modules/mod_authz_host.so
apache2 1467 root  mem    REG    8,1    22544 114833022 /usr/lib/apache2/modules/mod_authz_core.so
apache2 1467 root  mem    REG    8,1    10256 114833069 /usr/lib/apache2/modules/mod_authn_file.so
apache2 1467 root  mem    REG    8,1    10256 114833074 /usr/lib/apache2/modules/mod_authn_core.so
apache2 1467 root  mem    REG    8,1    14352 114833010 /usr/lib/apache2/modules/mod_auth_basic.so
apache2 1467 root  mem    REG    8,1    14344 114833059 /usr/lib/apache2/modules/mod_alias.so
apache2 1467 root  mem    REG    8,1    10256 114833015 /usr/lib/apache2/modules/mod_access_compat.so
apache2 1467 root  mem    REG    8,1    14608 120590365 /lib/x86_64-linux-gnu/libdl-2.23.so
apache2 1467 root  mem    REG    8,1    18976 120586259 /lib/x86_64-linux-gnu/libuuid.so.1.3.0
apache2 1467 root  mem    REG    8,1   166032 120586445 /lib/x86_64-linux-gnu/libexpat.so.1.6.0
apache2 1467 root  mem    REG    8,1    39224 120590381 /lib/x86_64-linux-gnu/libcrypt-2.23.so
apache2 1467 root  mem    REG    8,1  1868984 120590390 /lib/x86_64-linux-gnu/libc-2.23.so
apache2 1467 root  mem    REG    8,1   138696 120590374 /lib/x86_64-linux-gnu/libpthread-2.23.so
apache2 1467 root  mem    REG    8,1   204936 114559448 /usr/lib/x86_64-linux-gnu/libapr-1.so.0.5.2
apache2 1467 root  mem    REG    8,1   159488 114559436 /usr/lib/x86_64-linux-gnu/libaprutil-1.so.0.5.4
apache2 1467 root  mem    REG    8,1   456632 120586274 /lib/x86_64-linux-gnu/libpcre.so.3.13.2
apache2 1467 root  mem    REG    8,1   162632 120590373 /lib/x86_64-linux-gnu/ld-2.23.so
apache2 1467 root  DEL    REG    0,5              22729 /dev/zero
apache2 1467 root    0r   CHR    1,3      0t0         6 /dev/null
apache2 1467 root    1w   CHR    1,3      0t0         6 /dev/null
apache2 1467 root    2w   REG    8,1      279  93324825 /var/log/apache2/error.log
apache2 1467 root    3u  sock    0,8      0t0     20189 protocol: TCP
apache2 1467 root    4u  IPv6  20190      0t0       TCP *:http (LISTEN)
apache2 1467 root    5r  FIFO   0,10      0t0    159430 pipe
apache2 1467 root    6w  FIFO   0,10      0t0    159430 pipe
apache2 1467 root    7w   REG    8,1        0  93336006 /var/log/apache2/other_vhosts_access.log
apache2 1467 root    8w   REG    8,1        0  93323356 /var/log/apache2/access.log



 BAM!  The last two 7 lines show 3 log files worth investigating.  It also shows shared libraries, open sockets,..... The utility essentially examines the /proc virtual file system, which you could do instead if you wished, but lsof is significantly more convenient.

Happy seeking, hope this helps.  It's particularly been useful to me these past few days on a business-logic seeking mission of my own.