0

I am trying to run a single command to change all .DS_Store files within a directory and all subdirectories recursively.

So far, I have been using the following commands to find the files, set their date modified to Jan 1 01:01:01 2001, and then to find them and set their permissions to read only for user, group, and everyone .

find . -name '.DS_Store' -print0 | xargs -0 sudo touch -t 200101010101
find . -name '.DS_Store' -print0 | xargs -0 sudo chmod 444

I'm guessing there is a more efficient way to do this with a single command, but I'm not sure how to use the output of the find command in more than a single following command.

Ideally, if there is any error/failure encountered with any of the files/commands, I would want to have the command line stop and output the error message rather than continuing to execute. I've seen elsewhere that using && to separate commands on a single line would do this, but am not sure how to use this in conjunction with the output from the find command.

This question is about a smaller part of a larger task I'm trying to accomplish. I've almost got it all working now except part of the script that only seems to work when I run it from terminal directly. Here's a link to the main issue in case anyone here might be able to help: Service to execute series of commands on selected folders to eliminate issues with .DS_Store files in Finder

MikMak
  • 2,047
  • 2
  • 13
  • 19
  • 1
    Do you want it to be more efficient in terms of typing (shorter command), or in terms of execution time (CPU/IO usage)? – u1686_grawity Jul 20 '22 at 13:46
  • Ideally, execution time. length of command not as important. thanks! – MikMak Jul 20 '22 at 17:22
  • Does macOS come with Python (or at least Perl) these days? Most methods based on `find` would either negate the benefit of 'xargs' (running two commands _per file_) or wouldn't have sufficient error checking. – u1686_grawity Jul 20 '22 at 19:06
  • I have no idea what "these days" you refer to specifically, but I'm a die-hard holdout still using macOS Sierra (10.12.6). Maybe that would help to answer that question? :) In case not, here's a ink to a screenshot of the relevant results found on my system using *Find Any File*. https://i.imgur.com/AiHD8Ed.png – MikMak Jul 21 '22 at 00:33

2 Answers2

2

You can do that by using the -exec option and using its output as argument for a subshell:

find . -name '.DS_Store' -exec zsh -c 'touch -t 200101010101 "$1" && sudo chmod 444 "$1" || echo "$1" >> errors.txt' zsh {} \;

As you can see I use zsh {} to pass an argument to zsh -c, which allows me to use the output of find via $1. Note that you need to use double quotes here, e.g. to avoid filenames containing spaces causing problems.

I use && to only continue with the next command, if the previous exited without an error.

If an error occurs, || allows me to run a command on failure, for example writing the filename into a errors.txt file.

mashuptwice
  • 2,929
  • 2
  • 12
  • 25
  • @MikMak I am not aware of any difference in particular, but I've read about some arguments behaving different as well as some missing/different functionality. As I couldn't test this on a MacOS or BSD machine yet, I've added the note. – mashuptwice Jul 20 '22 at 18:34
  • @MikMak as you've accepted the answer, I suppose it works on MacOS without any problems. I will remove the note then. – mashuptwice Jul 21 '22 at 18:39
1

How about:

find . -name '.DS_Store' -exec sudo touch -t 200101010101 {} + -exec sudo chmod 444 {} + -print

(remember, -exec with + builds a single command with all files as arguments)

Paul
  • 895
  • 2
  • 11