0

I created several scripts to move messages tagged via notmuch notmuch between Maildir directories.

Having multiple accounts, I have some issues with the names of some directories mbsync created when syncing with gmail.

Here is my typical Maildir structure for gmail accounts:

gmail-user2
├── [Gmail]
│   ├── Brouillons
│   │   ├── cur
│   │   ├── new
│   │   └── tmp
│   ├── Corbeille
│   │   ├── cur
│   │   ├── new
│   │   └── tmp
│   ├── Messages envoy&AOk-s
│   │   ├── cur
│   │   ├── new
│   │   └── tmp
│   ├── Spam
│   │   ├── cur
│   │   ├── new
│   │   └── tmp
│   └── Tous les messages
│       ├── cur
│       ├── new
│       └── tmp
└── Inbox
    ├── cur
    ├── new
    └── tmp

When I run the following script:

function safeMove { s=${1##*/}; s=${s%%,*}; mv -f $1 $2/$s; }
echo Moving $(notmuch count --output=files tag:trash and tag:gmail-user2 and not path:/.*/Corbeille/ and not path:/.*/Trash/) trash-tagged mails to gmail-user2 Trash folder
for i in $(notmuch search --output=files tag:trash and tag:gmail-user2 and not path:/.*/Corbeille/ and not path:/.*/Trash/); do
    safeMove $i ~/Maildir/gmail-user2/[Gmail]/Corbeille/cur
done

I have the following error:

mv: cannot stat '/home/user1/Maildir/gmail-user2/[Gmail]/Tous': No such file or directory
mv: cannot stat 'les': No such file or directory

How could I make mv find the Tous les messages directory?

EDIT:

According to the links provided in the comments I added double quotes:

function safeMove { s=${1##*/}; s=${s%%,*}; mv -f $1 $2/$s; }
for i in "$(notmuch search --output=files tag:trash and tag:gmail-user2 and not path:/.*/Corbeille/ and not path:/.*/Trash/)"; do
    safeMove "$i" ~/Maildir/gmail-user2/[Gmail]/Corbeille/cur
done

Now I have the following error:

mv: target '/home/user1/Maildir/gmail-user2/[Gmail]/Corbeille/cur/1715687008.294831_40.user1-um450': No such file or directory

When find ~/Maildir/ -name 1715687008.294831_40.user1* gives:

/home/user1/Maildir/gmail-user2/[Gmail]/Tous les messages/cur/1715687008.294831_40.user1-um450,U=40:2,FS
15
  • 2
    This and this. Commented May 15 at 20:52
  • 1
    And: mywiki.wooledge.org/WordSplitting
    – ilkkachu
    Commented May 15 at 21:25
  • 1
    I'm not familiar with notmuch, but the documentation mentions a text0 format option that would allow you to do readarray -t -d '' files < <(notmuch search --output=files --format=text0 ...) to assemble the list of files unambiguously into an array... Commented May 15 at 23:01
  • 1
    ... You can then access the count as echo "Moving ${#files[@]} trash-tagged mails to gmail-user2 Trash folder" and loop over as for i in "${files[@]}"; do stuff with "$i"; done avoiding having to search twice. Commented May 15 at 23:02
  • 2
    Have you fixed the quotes inside the safemove function mv -f "$1" "$2/$s" or just in the loop "$i"? Commented May 15 at 23:35

1 Answer 1

3

I can't be sure here since I don't know what the output of this "notmuch" command is. However, as a general rule, you should avoid using for loops for things like this. See bash pitfall number 1. Instead, try with a while:

safeMove() { s=${1##*/}; s=${s%%,*}; mv -f -- "$1" "$2/$s"; }
notmuch search --output=files \
  tag:trash and tag:gmail-user2 and \
   not 'path:/.*/Corbeille/' and not 'path:/.*/Trash/' |
  while IFS= read -r i; do
    safeMove "$i" ~/'Maildir/gmail-user2/[Gmail]/Corbeille/cur'
  done

Note that this will still fail if your file names can contain newline characters. However, it looks like notmuch has an option for null-separated output, which means this should work:

 notmuch search --format=text0 --output=files \
  tag:trash and tag:gmail-user2 and \
   not 'path:/.*/Corbeille/' and not 'path:/.*/Trash/' |
  while IFS= read -rd '' i; do
    safeMove "$i" ~/'Maildir/gmail-user2/[Gmail]/Corbeille/cur'
  done
4
  • For Moving 209 trash-tagged mails to user2 Trash folder. I have 209 messages: mv: cannot stat '/home/user1/Maildir/user2/Inbox/cur/1715688956.313930_5730.user1-um450,U=5730:2,S': No such file or directory etc. However, it seems to work. Thank you!
    – crocefisso
    Commented May 16 at 11:39
  • And does that file exist? What is the output of ls '/home/user1/Maildir/user2/Inbox/cur/1715688956.313930_5730.user1-um450,U=5730:2,S'? We can't see your file names, so it is really hard for us to debug this. Can you provide us with an example directory and file names? Something simple that we can use to reproduce?
    – terdon
    Commented May 16 at 11:42
  • I just replaced two different aliases by user1 and user2 all the rest are full copy-pastes. I notice that the script behavior is erratic, sometimes I have mv: cannot stat messages, sometimes I have no messages, and sometimes I have messages saying mv: '.../Maildir/user1/[Gmail]/Spam/cur/1715688925.313930_5446.user1-um450' and '/home/user1/Maildir/gmail-user1/[Gmail]/Spam/cur/1715688925.313930_5446.user1-um450' are the same file. I think the problem comes from Google and what mbsync receives from it.
    – crocefisso
    Commented May 16 at 11:57
  • I found the issue, it didn't come from Google, but from another part of my script (the code discussed here is just a small part of it). Your code works perfectly and there was actually two things to change in the code I posted in my question: the double quotes and the null-separated output. Thanks again!
    – crocefisso
    Commented May 16 at 12:12

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .