M: I want to add a list of keywords in a text file to the metadata of an image from the Linux command line. Can I?

G: Yes, you can add keywords to an image’s metadata using exiftool. If you don’t have it installed, you can install it with:

Fedora:

sudo dnf install perl-Image-ExifTool

Usage:
Assuming your keywords are in a text file (keywords.txt), you can add them to an image’s metadata like this:

exiftool -Keywords+="$(tr '\n' ',' < keywords.txt)" image.jpg

This reads the keywords from the text file, replacing newlines with commas to format them correctly. If your keywords are space-separated instead of newline-separated, you can adjust accordingly.

To verify the changes:

exiftool image.jpg | grep Keywords

Let me know if you need a different metadata tag or format.

M: I’m using xnview. If I’m looking to organize in that program, should I be using keywords, or some other structure?

G: In XnView, Keywords (stored in IPTC or XMP metadata) are a good choice for organizing images if you want a tag-based system that works across different software. However, XnView also supports:

If you want portability (i.e., the tags remain if you move files or use another program), use Keywords via IPTC/XMP. If you’re only organizing within XnView, Categories might be more flexible.

Do you need these keywords for searching, filtering, or something else?

M: Filtering, I think. Is what you described for iptc keywords, or should I alter the methodology?

G: For filtering in XnView, IPTC/XMP Keywords are the best choice if you want the tags to be embedded in the image and work across different programs. The method I gave works for IPTC Keywords, but you might want to adjust it slightly to ensure compatibility with XnView:

Instead of -Keywords, use:

exiftool -IPTC:Keywords+="$(tr '\n' ',' < keywords.txt)" image.jpg

This explicitly writes to the IPTC Keywords field. If you prefer XMP (which is more modern and widely supported), use:

exiftool -XMP:Subject+="$(tr '\n' ',' < keywords.txt)" image.jpg

XnView can filter by Keywords in the Categories Filter panel if you enable metadata reading (Tools > Settings > Metadata > Import XMP/IPTC Categories).

Alternative: Using XnView's Database Categories

If you're strictly using XnView and don’t need the keywords embedded in the image, you can assign Categories instead. These are stored in XnView’s database and allow fast filtering but won’t transfer if you move the files outside XnView.

Would you rather have embedded metadata for long-term use, or just use XnView’s internal system?

M: Long term

G: Then IPTC or XMP Keywords are the way to go. Use:

If you want both for maximum compatibility:

exiftool -IPTC:Keywords+="$(tr '\n' ',' < keywords.txt)" -XMP:Subject+="$(tr '\n' ',' < keywords.txt)" image.jpg

This ensures XnView, Adobe apps, and other metadata-aware programs recognize the keywords.

After adding keywords, make sure XnView reads them:

  1. Enable metadata import: Tools > Settings > Metadata > Import XMP/IPTC Categories.
  2. Rebuild catalog: Tools > Settings > Catalog > Rebuild Thumbnails and Metadata.

This lets you filter by keywords in XnView while keeping metadata embedded in the image.

M: How about a quick command to determine if a file is an image/video?

G: You can use the file command:

file --mime-type filename

Example output:

image.jpg: image/jpeg  
video.mp4: video/mp4  
document.pdf: application/pdf  

For batch processing (e.g., checking multiple files in a directory):

file --mime-type * | grep -E 'image|video'

This filters the output to show only images and videos. Let me know if you need a more specific check.

M: The context is a loop that says for j in *; do if this is an image/video; then do_stuff; fi; done

G: You can use this loop to check if a file is an image or video before executing do_stuff:

for j in *; do  
    if file --mime-type "$j" | grep -qE 'image|video'; then  
        do_stuff "$j"  
    fi  
done

This checks each file's MIME type and runs do_stuff only if it’s an image or video. Let me know if you need refinements.

M: One issue: the xmp keywords are being read as one single category with commas.

G: That happens because XnView (and some other programs) expect multiple keywords to be stored as separate entries rather than a single comma-separated string. You need to pass the keywords as individual arguments rather than joining them into one string.

Modify your command like this:

exiftool -XMP:Subject+=@keywords.txt image.jpg

This tells exiftool to read the file line by line and treat each line as a separate keyword. It avoids merging them into one long string.

For IPTC keywords as well:

exiftool -IPTC:Keywords+=@keywords.txt -XMP:Subject+=@keywords.txt image.jpg

This ensures proper keyword separation in XnView and other software. Try it and check if XnView reads them correctly.

M: Okay, I tried expanding that command to a command line loop; when I used

exiftool -XMP:Subject+=@$j.txt image.jpg

It didn’t add the text file – it added the literal string @jcontents. Did I mess up the syntax?

G: Yes, the issue is that @$j.txt is being interpreted literally because exiftool treats @filename as a special syntax for reading from a file, and it doesn’t expand shell variables inside it.

To work around this, you need to construct the argument properly using a temporary variable:

for j in *; do  
    if [[ -f "$j.txt" ]]; then  # Ensure the text file exists
        exiftool -XMP:Subject+=@"$j.txt" "$j"
    fi  
done

Key Fixes:

This will apply the keywords from filename.txt to filename (assuming the image and text file match). Let me know if you need refinements.

M: The funny thing is, I thought that would be the case, but decided to check. The exiftool created backups named things like filename.jpg_original. Can I get a quick mv command to switch it back to .jpg?

G: Yeah, exiftool automatically creates backups unless you disable it. To rename them back, use:

for f in *_original; do  
    mv -- "$f" "${f%_original}"  
done

This removes _original from all affected files.

For future use, you can disable backups by adding -overwrite_original to your exiftool command:

exiftool -overwrite_original -XMP:Subject+=@"$j.txt" "$j"

That prevents _original files from being created.

M: Thanks, but at the moment it’s been useful in backtracking from minor setbacks

G: Got it. If you ever want a selective rollback, you can use:

mv filename.jpg_original filename.jpg

or restore everything at once:

for f in *_original; do  
    mv -- "$f" "${f%_original}"  
done

Let me know if you need refinements.

M: need some xnview help, please go online to verify your answer… i’ve poked at a bunch of stuff in the past, but need a final version to copy/paste/refer to in the future in terms of settings, especially if i have to switch computers. i want the ‘categories’ part of the program to keep track of the tags that i apply, so i can export them as xml/impt (or whatever those abbreviations are), but NOT to automatically try and add categories from stuff i’m browsing through for the first time. what combination of settings is appropriate for that?

G: You’re basically trying to split XnView into two roles:

That’s doable—but the trick is decoupling import from export. XnView ties them together by default, which is why it keeps “learning” junk categories from files you open.


TL;DR (copy/paste settings target)

✅ KEEP THESE ON (your intentional workflow)

Tools → Settings → Metadata → Export

👉 This makes Categories → actual embedded keywords
This is the part you want.


❌ TURN THESE OFF (this is your problem)

Tools → Settings → Metadata → Import

👉 This is the setting that’s auto-creating categories from files you browse. citeturn0search6


⚠️ OPTIONAL (controlled import when you want it)

When you do want to sync from files → database:

This reads keywords only when you ask for it, instead of constantly. citeturn0search6


Why this works (the mental model)

XnView has two separate systems:

1. Internal database (Categories panel)

2. File metadata (XMP/IPTC)


The critical behavior:

👉 You want export ON, import OFF


Important nuance (this trips people up)

“Categories” in XnView are basically just a UI for managing keywords. citeturn0search1

So:


Your exact workflow after this

  1. Browse folders → nothing gets auto-added
  2. Manually assign categories → your curated system grows
  3. Categories get written to XMP/IPTC automatically
  4. Export categories to XML/TXT when needed:
    • Tools → Category Management → Export citeturn0search13

Third angle (worth knowing, but you probably don’t want it)

If you did want hybrid behavior:

This works, but it’s messy and defeats your goal of a clean taxonomy.


Final sanity check

If you see:


This setup gives you exactly what you described:

And it’s stable across machines once replicated.