CXATTCOPYBlocks and attributesVersion 1.0.0Tested in AutoCAD
Copy attribute values from one block to others
Takes the attribute values of one block and writes them into the other blocks you select, either all tags or just the ones you name.
How to use it
- Type CXATTCOPY and click the block to copy from.
- Select the blocks to fill.
- Enter the tags to copy, separated by commas, or - for all.
Good to know
- Only tags that exist in both blocks are copied.
Load it in AutoCAD
- Save the file anywhere you like.
- Type APPLOAD, pick the file and choose Load.
- Type the command name: CXATTCOPY.
To have it at every start, add it to the Startup Suite in the same dialog, or load a whole folder at once with the optional loader. About the optional loader
Tested in AutoCAD (full version). Other programs that run AutoLISP may work, but they are not tested.
The code
Plain text, nothing hidden. Copy it into a file named cxattcopy.lsp, or download it.
;;; ============================================================================
;;; CXATTCOPY - Copy attribute values from one block to others
;;; Version 1.0.0
;;; CADXChange LISP tools - https://cadxchange.net/resources/lisp/cxattcopy
;;; Tested in: AutoCAD (full version). Other programs that run AutoLISP may work but are not tested.
;;; Copyright (c) CADXChange. Free for personal and commercial use; do not resell or
;;; republish it as a collection. Terms: https://cadxchange.net/legal/lisp-terms
;;; ============================================================================
;; Copy attribute values from the block SOURCE to every block in TARGETS that has the same tags.
;; TAGS is a list of tag names, or nil for all tags of the source. Returns how many values were copied.
(defun cx:attcopy-run (source targets tags / n)
(setq n 0)
(foreach e targets
(if (/= e source)
(foreach tg (if tags tags (cx:tags (list source)))
(if (and (cx:att-find e tg) (cx:att-find source tg))
(progn
(cx:att-set e tg (cx:att-get source tg))
(setq n (1+ n)))))))
n)
(defun c:CXATTCOPY ( / *error* cx:sv r source ss tags answer n)
(setq *error* cx:err)
(setq r (entsel "\nClick the block to copy the attribute values from: "))
(cond
((null r))
((/= (cx:dxf 0 (entget (car r))) "INSERT") (princ "\nThat is not a block."))
((null (cx:attribs (car r))) (princ "\nThat block has no attributes."))
(T
(setq source (car r))
(princ (strcat "\nAttributes: " (cx:join (cx:tags (list source)) ", ")))
(if (setq ss (ssget '((0 . "INSERT") (66 . 1))))
(progn
(setq answer (cx:ask-str "Tags to copy, separated by commas (- for all)" "-"))
(setq tags (if (member answer '("-" "")) nil (mapcar 'cx:trim (cx:split answer ","))))
(cx:start '(("CMDECHO" . 0)))
(setq n (cx:attcopy-run source (cx:ss->list ss) tags))
(princ (strcat "\n" (itoa n) " value(s) copied."))
(cx:end)))))
(princ))
;;; ---- Shared helpers (used by the command above; identical in every CADXChange tool) ----
(defun cx:dxf (code ed)
(cdr (assoc code ed)))
;; The ATTRIB entities of a block reference, in order.
(defun cx:attribs (ins / e out)
(setq e (entnext ins))
(while (and e (= (cx:dxf 0 (entget e)) "ATTRIB"))
(setq out (cons e out) e (entnext e)))
(reverse out))
;; The attribute tags found in a list of block references, each once, in order of appearance.
(defun cx:tags (names / out)
(foreach e names
(foreach a (cx:attribs e)
(if (not (member (strcase (cx:dxf 2 (entget a))) (mapcar 'strcase out)))
(setq out (append out (list (cx:dxf 2 (entget a))))))))
out)
;; The ATTRIB of a block reference with the tag TAG (any case), or nil.
(defun cx:att-find (ins tag / r)
(foreach a (cx:attribs ins)
(if (and (not r) (= (strcase (cx:dxf 2 (entget a))) (strcase tag))) (setq r a)))
r)
;; Replace the text of a TEXT, ATTRIB, ATTDEF or MTEXT.
(defun cx:text-set (ename str / ed out)
(setq ed (entget ename))
(if (= (cx:dxf 0 ed) "MTEXT")
(progn
;; MTEXT stores text in 250-character pieces: 3 groups for the first pieces, 1 for the last
(foreach p ed (if (not (member (car p) '(1 3))) (setq out (cons p out))))
(setq out (reverse out))
(while (> (strlen str) 250)
(setq out (append out (list (cons 3 (substr str 1 250)))) str (substr str 251)))
(setq ed (append out (list (cons 1 str)))))
(setq ed (subst (cons 1 str) (assoc 1 ed) ed)))
(entmod ed)
(entupd ename)
T)
;; Set the attribute TAG of a block reference to VALUE; returns T when the block has that attribute.
(defun cx:att-set (ins tag value / a)
(if (setq a (cx:att-find ins tag))
(progn (cx:text-set a value) (entupd ins) T)))
(defun cx:att-get (ins tag / a)
(if (setq a (cx:att-find ins tag)) (cx:dxf 1 (entget a))))
;; Close the undo step and put the system variables back.
(defun cx:end ()
(foreach v cx:sv (setvar (car v) (cdr v)))
(command "_.UNDO" "_End")
(setq cx:sv nil)
(princ))
;; Error handler: restores everything, stays quiet on Esc.
(defun cx:err (msg)
(if (> (getvar "CMDACTIVE") 0) (command))
(cx:end)
(if (and msg (not (wcmatch (strcase msg) "*BREAK*,*CANCEL*,*EXIT*")))
(princ (strcat "\nError: " msg)))
(princ))
(defun cx:join (lst delim / out)
(setq out "")
(foreach s lst
(setq out (if (= out "") s (strcat out delim s))))
(if lst out ""))
;; Ask for a string (spaces allowed) with a default; "-" means an empty string.
(defun cx:ask-str (msg def / v)
(setq v (getstring T (strcat "\n" msg " <" def ">: ")))
(cond ((= v "") def) ((= v "-") "") (T v)))
;; Remove spaces and tabs at both ends.
(defun cx:trim (s / a b)
(setq a 1 b (strlen s))
(while (and (<= a b) (member (substr s a 1) '(" " "\t"))) (setq a (1+ a)))
(while (and (>= b a) (member (substr s b 1) '(" " "\t"))) (setq b (1- b)))
(substr s a (max 0 (1+ (- b a)))))
;; Split STR at every DELIM (a one-character string) into a list of strings.
(defun cx:split (str delim / out cur i c)
(setq out nil cur "" i 1)
(while (<= i (strlen str))
(setq c (substr str i 1))
(if (= c delim)
(setq out (cons cur out) cur "")
(setq cur (strcat cur c)))
(setq i (1+ i)))
(reverse (cons cur out)))
;; Save the system variables in VARS ((name . value) ...), set them, and open one undo step.
;; The caller declares cx:sv as a local so the saved values live exactly as long as the command.
(defun cx:start (vars)
(setq cx:sv (mapcar (function (lambda (v) (cons (car v) (getvar (car v))))) vars))
(command "_.UNDO" "_Begin")
(foreach v vars (setvar (car v) (cdr v)))
(princ))
;; Selection set to a list of entity names.
(defun cx:ss->list (ss / i out)
(setq i 0)
(if ss
(repeat (sslength ss)
(setq out (cons (ssname ss i) out)
i (1+ i))))
(reverse out))
(princ "\nCXATTCOPY loaded. Type CXATTCOPY to start.")
(princ)
Version history
Version 1.0.0 · October 27, 2026
First release.
Need this across many drawings?
BlockSync does this kind of job on a whole folder of drawings in one run.
BlockSyncMore commands: Blocks and attributes
CXATTFILLFill an attribute in many blocksCXATTNUMNumber a block attribute in sequenceCXATTVISShow or hide block attributesCXBLKCOUNTCount blocks by nameCXBLKREPLReplace blocks with another blockCXBLKRENRename blocks in bulkCXBLKXPExplode blocks but keep their attribute textCXBLKSELSelect blocks by name or attribute valueCXBLKATInsert a block at many places at once