Free Google Sheets scripts

Copy, paste, use. Free for any use. Want one customized? Ask for a Fix or Automate.

Send an email when a cell changes

Emails you when someone sets a status column to "Done". In your sheet: Extensions → Apps Script, paste this, change the settings at the top, save, then add an installable "On edit" trigger for notifyOnEdit (Triggers → Add trigger). Simple triggers can't send email.

// SheetHand free script: email on status change
const SHEET_NAME = 'Sheet1';     // tab to watch
const STATUS_COL = 5;            // column E
const TRIGGER_VALUE = 'Done';
const NOTIFY = 'you@example.com';

function notifyOnEdit(e) {
  const range = e.range;
  const sheet = range.getSheet();
  if (sheet.getName() !== SHEET_NAME || range.getColumn() !== STATUS_COL) return;
  if (String(range.getValue()).trim() !== TRIGGER_VALUE) return;
  const row = range.getRow();
  const values = sheet.getRange(row, 1, 1, sheet.getLastColumn()).getDisplayValues()[0];
  MailApp.sendEmail(NOTIFY,
    `Row ${row} marked ${TRIGGER_VALUE}`,
    `Sheet: ${sheet.getName()}
Row ${row}: ${values.join(' | ')}

${e.source.getUrl()}`);
}

Back up a sheet to Drive every night

Makes a dated copy of the whole spreadsheet in a Drive folder. Paste, set the folder ID, then add a time-driven trigger for nightlyBackup (Day timer).

// SheetHand free script: nightly backup copy
const FOLDER_ID = 'PASTE_FOLDER_ID';  // from the folder URL
const KEEP_DAYS = 30;

function nightlyBackup() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const folder = DriveApp.getFolderById(FOLDER_ID);
  const stamp = Utilities.formatDate(new Date(), Session.getScriptTimeZone(), 'yyyy-MM-dd');
  DriveApp.getFileById(ss.getId()).makeCopy(`${ss.getName()} backup ${stamp}`, folder);
  const cutoff = Date.now() - KEEP_DAYS * 86400000;
  const files = folder.getFiles();
  while (files.hasNext()) {
    const f = files.next();
    if (f.getName().startsWith(`${ss.getName()} backup`) && f.getDateCreated().getTime() < cutoff) f.setTrashed(true);
  }
}

Remove duplicate rows with one click

Adds a SheetHand menu with "Remove duplicates" that keeps the first row of each duplicate set, based on the columns you choose.

// SheetHand free script: dedupe menu
const KEY_COLS = [1, 2]; // dedupe on columns A and B

function onOpen() {
  SpreadsheetApp.getUi().createMenu('SheetHand').addItem('Remove duplicates', 'removeDuplicates').addToUi();
}

function removeDuplicates() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const last = sheet.getLastRow();
  if (last < 3) return;
  const range = sheet.getRange(2, 1, last - 1, sheet.getLastColumn());
  const seen = new Set(), keep = [];
  range.getValues().forEach(r => {
    const key = KEY_COLS.map(c => String(r[c - 1]).trim().toLowerCase()).join('||');
    if (!seen.has(key)) { seen.add(key); keep.push(r); }
  });
  const removed = last - 1 - keep.length;
  range.clearContent();
  sheet.getRange(2, 1, keep.length, keep[0].length).setValues(keep);
  SpreadsheetApp.getUi().alert(`Removed ${removed} duplicate row(s).`);
}

Test on a copy first. Provided as-is, without warranty.